2014-09-06 19 views
1

與線程相比,我不太瞭解異步/等待好處。異步/等待方法中耗時的任務

如果在一個方法中,我有一個沒有異步/等待版本的操作,在其他異步操作過程中消耗一些時間,如20ms。

調用這個方法1000次,因爲異步/等待只在一個線程內執行,它會在我的測試中使用至少20ms×1000次(使用Thread.Sleep來模擬)。

對異步/等待有任何誤解嗎?

public void Start() 
{ 
    Task[] task = new Task[500]; 

    for (int i = 0; i < 500; i++) 
    { 
     task[i] = AsyncGet(); 
    } 

    await Task.WhenAll(task); 
} 
public async Task<bool> AsyncGet() 
{ 
    HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://www.example.com"); 
    req.Method = "GET"; 
    using(HttpWebResponse res = (HttpWebResponse)await req.GetResponseAsync()) 
    { 

     Thread.Sleep(20);//Simulate an operation without async/await version that consume a little bit time 

     using (Stream responseStream = res.GetResponseStream()) 
     { 
      StreamReader sr = new StreamReader(responseStream, Encoding.ASCII); 
      string resp = await sr.ReadToEndAsync(); 
     } 
    } 
} 

更新問題

如果我有必要做一些非異步/等待操作就像使用外部庫獲取HTTP響應後操縱JSON。避免浪費時間的最佳做法是什麼?

+0

爲什麼只有1個線程,它使用線程池中的任何可用線程。 – 2014-09-06 10:26:59

+0

您通常會盡量避免使用非異步持久操作。或者在同步上下文無關緊要的情況下,可以在返回的Task對象上使用'ConfigureAwait(false)'來讓繼續在任何上下文繼續。 – Dirk 2014-09-06 10:27:47

+0

@eranotzap發佈的代碼在1個線程上運行。 'GetResponseAsync'可能在內部使用另一個線程,但考慮到這是一個IO操作,它更有可能使用類似IO完成端口的東西。 – Dirk 2014-09-06 10:29:48

回答

4

不,不一定。如果您從調用Start方法SynchronizationContext這是單線程(說任何UI上下文,如WindowsFormsSynchronizationContext或DispatcherSynchronizationContext)答案是yes [*]。

如果您從其他某個線程(稱爲工作線程)調用Start。它不會需要20ms x 1000。因爲在這種情況下,等待後的代碼將在ThreadPool thread中執行。所以在等待之後可以有N個線程執行代碼。你的情況下N將小於或等於500。

這就是說,你不應該阻止異步方法。如果你需要使用異步方法中的任何同步代碼,您應該使用await Task.Run(()=> CodeThatBlocks())

* 如果的SynchronizationContext不SynchronizationContext.SetSynchronizationContext

0

睡一個線程覆蓋將還在沉睡的線程,無論任何異步電動機/等待結構。 async/await的思想是儘可能地保持每個線程的佔用,以避免創建大量線程來等待I/O。在後臺執行CPU密集型操作仍然需要您創建線程。

換句話說,您提供的示例將導致線程凍結20 ms,無論它不是async/await的預期用例。

換句話說,無論是:

  • 只需使用異步I/O。不要做任何CPU密集型處理(也不要使用Sleep)。
  • 使用線程池。
2

是否有任何關於異步/等待的誤解?

是的,你似乎有一個誤解。您無法模擬Thread.Sleep這是阻止呼叫的async-await行爲。當你await Task.WhenAll,多個異步請求併發執行,忽略底層線程。

如果您想同時卸載多個請求,請使用await Task.Delay進行測試,您會注意到行爲的變化。

您可以調用GetResponseAsync當了一步藉此和使用ConfigureAwait(false)所以你的方法不會名帥工作的持續回原來的SynchronizationContext(通過自定義的,如WinFormSynchronizationContext打交道時,這點很重要),並繼續在IOCP上執行。

+1

您的回答也非常有幫助。我希望我能設置兩個接受的答案 – 2014-09-06 10:39:38