2013-08-31 42 views
0

我有看起來像這樣的方法:如何安全地等待異步方法?

static async Task<string> GetGooglePage(ProxyInfo proxy) 
    { 
     using (var m=new MyNetworkClass()) 
     { 
      m.Proxy = proxy; 
      return await m.GetAsync("http://www.google.com"); 
     } 
    } 

現在我想從一個沒有異步方法調用它,並得到結果。

我試圖做到這一點是這樣的方式:

 foreach (var proxy in proxys) 
     { 
      try 
      { 
       GetGooglePage(proxy.ToProxyInfo()).Wait(); 
      } 
      catch 
      {} 
      lock (Console.Out) 
      { 
       Console.Write(current++ + "\r"); 
      } 
     } 

我的問題是,有時GetGooglePage(proxy.ToProxyInfo()).Wait();死鎖(根據Visual Studio調試器,不存在堆棧超出此調用)。

我不想使用異步一直到Main()。如何從同步代碼中正確調用GetGooglePage而不會冒死鎖?

+0

我沒有看到任何導致死鎖的代碼。它是如何發生的? –

+1

@SriramSakthivel原因在於問題。 GetGooglePage的'return'在主線程可用之前沒有機會運行,但它不會變得可用,因爲它正在等待'GetGooglePage'完成。 – hvd

+0

你爲什麼在你的例子中鎖定?用你寫代碼的方式,它是以串行方式運行的。 – Matthew

回答

2

最好的選擇不是這樣做的:如果你同步等待一個異步方法,沒有理由讓該方法在第一個地方是異步的。所以,你應該做什麼(假設你真的想要或者必須使頂層方法同步)是使所有方法同步。

如果你不能做到這一點,那麼你可以通過使用ConfigureAwait(false)防止死鎖:

static async Task<string> GetGooglePage(ProxyInfo proxy) 
{ 
    using (var m=new MyNetworkClass()) 
    { 
     m.Proxy = proxy; 
     return await m.GetAsync("http://www.google.com").ConfigureAwait(false); 
    } 
} 

這樣,當方法恢復,也不會嘗試恢復在UI線程上,所以你不會陷入僵局。如果您在GetAsync()或其調用的方法中使用await,則您也需要在那裏執行相同的操作。

一般而言,只要您不需要恢復UI線程,在任何地方都可以使用ConfigureAwait(false)

4

您遇到了我describe on my blog的死鎖。

async方法中沒有用於阻塞的標準解決方案,因爲沒有適用於所有情況的解決方案。唯一正式推薦的解決方案是始終使用async。 Stephen Toub有一個good summary of workarounds here

一種方法是在後臺線程(通過Task.Run),然後在Wait上執行async方法。這種方法的缺點是:1)它不適用於需要特定上下文的方法(例如,寫入ASP.NET響應或更新UI); 2)從同步上下文更改爲非同步線程池上下文可能會引入競爭條件; 3)它只是在等待另一個線程而燒燬一個線程。

另一種方法是在嵌套消息循環內執行async方法。這種方法的缺點是:1)對於每個平臺(WPF與WinForms等),「嵌套消息循環」是不同的;和2)嵌套循環引入了重入問題(這已經導致了Win32時代的許多錯誤)。