簡體   English   中英

當異步代碼嘗試在已經執行的線程上恢復時會發生什么?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM