2017-02-14 64 views
3

在ASP.NET 4.5應用程序中,哪些更適合從同步方法調用異步方法?Task.Run vs null SynchronizationContext

var result = Task.Run(() => SomethingAsync()).GetAwaiter().GetResult(); 

// or 

var temp = SynchronizationContext.Current; 
try 
{ 
    SynchronizationContext.SetSynchronizationContext(null); 
    return SomethingAsync().GetAwaiter().GetResult(); 
} 
finally 
{ 
    SynchronizationContext.SetSynchronizationContext(temp); 
} 

注:是的,我知道我應該使用async/await一路走低,但我問的是最底層,和ASP.NET核心外的過濾器和剃鬚刀的觀點不是異步,所以如果我想從過濾器或剃刀視圖調用異步方法,我需要以某種方式同步它。由於SynchronizationContext,只是使用SomethingAsync().GetAwaiter().GetResult()導致死鎖,所以我需要一種方法來運行此代碼而不需要SynchronizationContext

編輯 下面是簡單的輔助類,乾淨包裝這件事:

public static class Async 
{ 
    public static T Run<T>(Func<Task<T>> func) 
    { 
     var context = SynchronizationContext.Current; 
     if (context == null) 
     { 
      return func().GetAwaiter().GetResult(); 
     } 

     SynchronizationContext.SetSynchronizationContext(null); 

     try 
     { 
      return func().GetAwaiter().GetResult(); 
     } 
     finally 
     { 
      SynchronizationContext.SetSynchronizationContext(context); 
     } 
    } 

    public static void Run(Func<Task> func) 
    { 
     var context = SynchronizationContext.Current; 
     if (context == null) 
     { 
      func().GetAwaiter().GetResult(); 
      return; 
     } 

     SynchronizationContext.SetSynchronizationContext(null); 

     try 
     { 
      func().GetAwaiter().GetResult(); 
     } 
     finally 
     { 
      SynchronizationContext.SetSynchronizationContext(context); 
     } 
    } 
} 

// Example 
var result = Async.Run(() => GetSomethingAsync("blabla")); 
+0

'Task.Run'方法稍微更明顯一些,'SynchronizationContext.Current'方法稍微有點高性能。哪一個「更好」純粹是一個意見問題。 –

+0

我應該說「更好」。好的,所以在我看來,第二種情況沒有什麼問題,所以如果它包含在一個漂亮的靜態函數中,聽起來應該很好用 – Marius

+0

你不應該需要'if'檢查。 'SetSynchronizationContext(null)'是有效的。 –

回答

1

顯然,這是不理想的同步調用異步代碼,但是如果你要我想說避免Task.Run,如果你能幫助它。

Task.Run擁有SetSynchronizationContext(null)一些問題:

  • 爲什麼要使用它,你要開始一個Web服務器上的新任務,目前尚不清楚?如果沒有評論,這將被新開發者快速刪除。然後繁榮,難以診斷生產問題(僵局)。
  • 它從一個新的線程池線程開始,當它不需要時,直到它達到第一個等待完成的延續,vs同步運行。 (這是一個非常小的優化)
  • 如果你是從多個層面來保護你的SynchronizationContext這樣做,它會讓你同步阻止整個任務返回功能,而不僅僅是需要它的區域,你也會增加你的問題每次使用它。你將最終以加長阻止異步代碼,這當然不是你想要的。
  • 如果事實證明沒有阻塞/死鎖,你認爲有或後來修復,現在Task.Run正在引入阻塞異步,而SetSynchronizationContext(null)不會花費任何東西。

最後,另一個建議是use something like AsyncPump.Run (Stephen Toub)當你阻止Task返回函數。它等待排隊延續由阻塞線程運行,這樣您就不會支付多個併發線程的代價,並且不會發生死鎖,顯然還不如完全使用異步。