2013-10-23 49 views
6

讓我發佈一個簡單的例子:什麼線程在`await`關鍵字後面運行代碼?

private void MyMethod() 
    { 
     Task task = MyAsyncMethod(); 
     task.Wait(); 
    } 

    private async Task MyAsyncMethod() 
    { 
     //Code before await 
     await MyOtherAsyncMethod(); 
     //Code after await 
    } 

比方說,我在運行單線程應用程序般的控制檯APP-上面的代碼。我很難理解代碼//Code after await將如何運行。

我明白,當我打的await關鍵字MyAsyncMethod()控制追溯到MyMethod(),但後來我鎖定的線程與task.Wait()。如果線程被鎖定,如果線程被鎖定,如何運行//Code after await

是否創建了一個新線程來運行//Code after await?或者主線程神奇地走出task.Wait()運行//Code after await

我不知道這應該如何工作?

回答

11

如果從主線程調用,因爲您使用Wait()阻塞主線程,所發佈的代碼將在Winform應用程序中「死鎖」。

但在控制檯應用程序這個工程。但是如何?

答案被隱藏在SynchronizationContext.Current中。await捕獲「SynchronizationContext」,當任務完成時,它將繼續在同一個「SynchronizationContext」中。

在winform應用程序SynchronizationContext.Current將被設置爲WindowsFormsSynchronizationContext這將發佈到「消息循環」的調用,但誰來處理?主線程正在等待Wait()

在控制檯應用程序SynchronizationContext.Current不會被默認設定所以這將是null時沒有「的SynchronizationContext」可等待捕捉到,因此將安排在延續ThreadPool(TaskScheduler.Default這是ThreadpoolTask​​Scheduler)等代碼後等待作品(通過線程池線程)。

上述捕獲行爲可以使用Task.ConfigureAwait(false);進行控制,這將防止winform應用程序死鎖,但await之後的代碼不再在UI線程中運行。

+0

這一切都假設當前的同步上下文永遠不會被用戶代碼改變;你可以在任何你想要的任何應用程序中設置它,並給它任何你想要的行爲。 – Servy

+0

我不知道.NET控制檯應用程序背後的線程池。意識到現在可以回答所有問題。謝謝。 – AxiomaticNexus

+1

@YasmaniLlanes如果您不瞭解「ThreadPool」,我建議您閱讀它。它應該是這樣學習ThreadPool然後TPL然後異步/等待。 –

10

是否有一個新的線程被創建來運行//等待後的代碼?

也許吧。也許不會。在等待表達式開始時,Task的等待模式實現使用同步上下文(它是「當前」)運行延續(位於await表達式之後的位)。例如,如果您處於UI線程的上下文中,那意味着您將最終返回到相同的UI線程。如果你在一個線程池線程中,你最終會回到的一些線程池線程,但它可能是一個不同的線程池線程。

當然你的代碼示例,如果你在一個UI線程的時候,你要Wait()調用將會阻塞UI線程,以便延續無法運行 - 你需要小心一點。 (電話上,你不知道要完成,這可能需要在當前線程上的工作任務Wait()Result,是一個壞主意。)

請注意,您可以撥打Task.ConfigureAwait,這樣就可以表達的意圖的而不是要求繼續在相同的上下文。這通常是適合哪個不關心它們運行在哪個線程庫方法:

await task.ConfigureAwait(false); 

(它影響比線程多 - 這是其被捕獲或不整情況下。)

我想等待着熟悉發生了什麼是一個好主意。網上有大量的文檔,如果你允許我簡單的插件,還有3rd edition of C# in Depth和我的Tekpub screencast series on the topic。或從MSDN開始並從那裏開始。

+0

好吧,我理解在WCF應用程序或線程池中的UI線程環境中的行爲,就像在Web應用程序中一樣?像控制檯應用程序這樣的單線程應用程序會發生什麼? – AxiomaticNexus

+1

@Jon:延續實際上排隊到當前的'SynchronizationContext',落在當前的'TaskScheduler'上。這是因爲'TaskScheduler.Current'很古怪,不太直觀。 –

+2

@YasmaniLlanes:實際上沒有單線程.NET應用程序這樣的東西;每個.NET應用程序都有線程池。因此,在這種情況下(當沒有當前的'SynchronizationContext'或'TaskScheduler')時,延續會排隊到線程池。 –

相關問題