[英]What happens to the thread when reaching 'await' on 'async' method?
[英]What happens when async code attempts to resume on a thread that is already executing?
我覺得答案是由於我對線程如何工作有一個不正確的概念,但是可以理解。
private void button1_Click(object sender, EventArgs e)
{
this.TestMethodAsync(); // No await, i.e. fire and forget
// ** Some code here to perform long running calculation (1) **
}
private async Task TestMethodAsync()
{
// Some synchronous stuff
await Task.Delay(1000);
// ** Some code here to perform long running calculation (2) **
}
首先,我不會“解雇”這樣的異步方法(我將使用Task.Run),但是我遇到了這樣做的代碼,並且我試圖了解其效果。
在使用WindowsFormsSynchronizationContext
的WinForms應用程序中,我對async和await的理解告訴我,當我單擊button1時,該方法將在UI線程上同步啟動。 它將調用TestMethodAsync
並同步運行,直到到達等待狀態。 然后它將捕獲上下文,啟動Task.Delay
任務,並將控制權交給調用者。 由於我們不在等待此調用,因此button1_Click
將在UI線程上繼續並開始執行計算(1)。
在某個時候, Task.Delay(1000)
將完成。 然后,繼續操作將使用捕獲的上下文運行TestMethodAsync
方法的其余部分,在這種情況下,這意味着繼續操作將在UI線程上運行。 現在將開始執行計算(2)。
現在,我們有兩個單獨的代碼段,希望同時在同一線程(UI線程)上運行。 我對此的調查似乎表明,線程要在兩個代碼段之間來回切換才能同時執行它們。
題:
我對這里到底發生了什么感到困惑。 如何在已經運行其他代碼的線程上恢復? 是什么迫使線程在要運行的代碼的兩個部分之間切換? 通常,當您嘗試在已經在運行其他代碼的線程上恢復時會發生什么?
(我想這與我的click事件首先在UI線程上運行沒有什么不同,就我所知,它在UI線程上運行,而且我知道UI線程也在做其他事情,但是我之前從未真正考慮過這個問題。)
這是您不了解的秘密:我給您Windows消息循環
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
while(TRUE)
{
bRet = GetMessage(&msg, NULL, 0, 0);
if (bRet <= 0) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
這是您應用程序的實際“主”。 您只是看不到它,因為它被隱藏在幕后。
無法想象有一個更簡單的循環。 它從隊列中獲取一條消息。 如果沒有更多消息,則必須完成程序。 如果有消息,那么它將運行標准消息翻譯並調度該消息,然后繼續運行。
如何在已經運行其他代碼的線程上恢復?
不是。 “在正在運行其他代碼的線程上繼續”實際上是在隊列中放入一條消息 。 DispatchMessage正在同步調用該“其他代碼”。 完成后,它返回循環,對隊列進行輪詢,並且該消息指示下一步需要分派哪些代碼。 然后,它會同步運行,直到返回到循環。
是什么迫使線程在要運行的代碼的兩個部分之間切換?
沒有。 那不會發生。
通常,當您嘗試在已經在運行其他代碼的線程上恢復時會發生什么?
描述需要運行哪些連續性的消息排隊。
我想這與我的click事件首先在UI線程上運行沒有什么不同,就我所知,它在UI線程上運行,我知道UI線程也在做其他事情,但是我之前還沒有真正考慮過。
開始考慮。
點擊事件完全相同。 您的程序正在做某事; 您單擊鼠標; 單擊處理程序不會中斷UI線程並開始在其上運行新工作。 而是將消息排隊,並且當您的UI線程控件返回到消息循環時,最終會處理該單擊。 DispatchMessage導致通過Windows窗體中的某種機制來調用Button1_OnClick。 這是的WinForms 是什么; 一種將Windows消息轉換為對C#方法的調用的機制。
但是你已經知道了 。 您知道,當事件驅動程序執行長時間運行的同步操作時,UI會凍結,但最終會處理click事件。 您如何看待這種情況? 您一定已經了解了它們正在排隊等待稍后處理,對嗎?
練習: DoEvents
做什么的?
練習:根據您現在所知道的:如果您在循環中調用DoEvents
來解除阻止UI,可能會出錯嗎?
練習: await
與GUI應用程序中的DoEvents
有何不同?
如何在已經運行其他代碼的線程上恢復?
需要對其進行專門設計以支持它。 需要有一個適當的框架,該框架允許線程進行工作,然后在以后的某個時間點執行該工作。
這就是您的UI線程的工作方式。 它有一個隊列,每當您安排要在UI線程中完成的工作時,就在隊列末尾添加一個項目。 然后,UI線程從隊列中獲取第一個項目,執行它,然后在完成后轉到下一個項目,依此類推,直到結束應用程序。
是什么迫使線程在要運行的代碼的兩個部分之間切換?
沒什么,因為它沒有做到這一點。 它運行一個,然后在完成時運行另一個。
通常,當您嘗試在已經在運行其他代碼的線程上恢復時會發生什么?
有人編寫了一些自定義代碼專門用於執行此操作,在這種情況下,它會執行該代碼明確要求執行的任何操作,否則您將無法執行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.