2017-04-21 32 views
3

當一個async方法awaits a Task它正在運行的線程會發生什麼?線程在等待某事時發生了什麼

我猜測,在UI線程上,消息循環會恢復,並且在線程池線程上線程將釋放回線程池。但是如果線程手動啓動會發生什麼?還有其他類型的線程嗎?

回答

2

這花了我很長時間才意識到,但異步等待的這一部分很簡單,只需上調用堆棧即可。任何時候一個方法awaits東西(假設它不是一個完整的任務或類似的東西)它返回給調用者。由於我們談論的是代碼放棄控制線程的地方,這意味着這是代碼堆棧頂部的最後一部分。

如果我們在U​​I線程上運行,我們會返回到消息循環。如果我們將線程池線程控制返回到線程池。手動創建的線程只能運行一個void方法,如果它是一個async void方法,則只能使用await,這意味着即使在方法完成之前,它也會在第一個await處結束線程。

延續工作方式相同。它會將其排列在UI線程或線程池中,然後再次或者awaits或完成,再次控制控制權。

編輯:我已經做了一些自定義任務調度器的實驗,你可以在那裏應用完全相同的邏輯。當任務等待它放棄控制。我使用的任務調度程序基於此single threaded task scheduler。在這種情況下,切入控制意味着任務調度程序開始處理隊列中的下一個任務。同樣重要的是要注意,除非使用ConfigureAwait(false),否則使用當前任務計劃程序計劃延續。

+1

這是一個不完整且部分不正確的答案。手動線程,線程池線程或任何線程都可以運行我們想要的任何異步方法,只需將其包裝到Task中,然後在線程上同步運行即可。 – ipavlu

+0

@ipvalu問題是當一個線程異步等待什麼時會發生什麼。同步等待任務完全是另一回事。 – BetaKeja

+0

你說手動創建的線程只運行異步void方法。線程沒有任何種類的同步上下文,調度程序。這樣的線程可以運行任何東西,甚至是異步任務方法。當你調用任何異步方法時,它就像Task.RunSynchronously一樣,只要它們完成了等待並且在所有同步部分在等待中完成或者它首先命中時沒有完成await(等待通常在線程池上運行休息)。 – ipavlu

-1

其實,任何線程都可以同步運行異步方法,它會等到

=>

斯蒂芬·克利裏指出,這部分是不正確的:「等到異步方法完成」。措辭實在是非常誤導。所以也許這會好一點。

=>

所有同步執行的部分都完成了。可能正在等待完成的任務,如Task.FromResult。另外,它可能是另一個異步方法,可以完全同步執行。

但是,在等待某個地方等待在提供awaiter之前無法完成的操作時,即使異步方法尚未運行到完成,t.RunSynchronously也將退出。

void ThreadWorker(object obj) 
{ 
    var t = new Task<Task>(AsyncMethod); 
    t.RunSynchronously(); 
    //in this case, it runs the AsyncMethod completely 
    //and effectively waits for result 
} 

async Task AsyncMethod() 
{ 
    await Something().ConfigureAwait(false); 
    //everything from here runs on the ThreadPool 
} 
+0

這個答案是錯誤的。 'RunSynchronously'等待任務't'完成;它不*等到從'AsyncMethod'返回的任務完成。 –

+0

是的,但有時沒有,這一切都取決於。我想,如果我寧願說,它會正確地等待異步方法中的所有同步處理的等待,包括等待已完成的任務,這些任務並不總是已知的,但不會等待異步方法完成。 – ipavlu

+0

等待的部分沒有措辭正確,因爲我只想指出,該線程不必只運行異步無效方法,但與t.RunSynchronously異步任務方法可以調用,以及... – ipavlu

0

請注意,沒有手動線程這樣的事情。他們都只是線程。像電子一樣,沒有兩種電子:)。

ThreadPool線程,你的線程,UI線程,COM線程等的區別在於線程是否有消息循環,同步上下文。 如果線程具有同步上下文,那麼我們在其上啓動的所有內容都不會直接運行,而是全部作爲來自某個隊列的委託啓動,並且當隊列中沒有任何內容時,線程正在等待。

因此,如果async方法中的代碼的某些部分在具有同步上下文的此類線程上運行,則等待它存儲上下文,除非ConfigureAwait並使用此方法完成並且正在等待下一個委託或消息執行。所以當await完成並且ConfigureAwait沒有被用來將下一個代碼移動到線程池時,它會發送到同步上下文新的委託,這是代碼在等待處理後的代碼。

相關問題