Dot-Net-4.5

非同步 - 留在目前執行緒上?

  • October 5, 2019

我讀過 Eric lippert 的文章,關於async, 以及人們對async關鍵字的困惑。他說 :

it( async) 表示“此方法包含涉及等待非同步操作的控制流,因此將由編譯器重寫為繼續傳遞樣式,以確保非同步操作可以在正確的位置恢復此方法。” 非同步方法的全部意義在於您盡可能多地停留在目前執行緒上

我不明白這一點。如果我執行一個非同步方法 ( Task) 並且它執行,它肯定會在另一個執行緒上執行。

此外,如果我編寫一個方法使用await,(恕我直言)它會釋放正常的控制流,並且程式碼會在稍後在另一個執行緒上以 類似的方式“重構”。ContinueWith

我用 (console) 測試了它:

/*1*/   public void StartChain()
/*2*/   {
/*3*/           var a = FuncA();
/*4*/           Console.WriteLine(a.Result);
/*5*/   }
/*6*/   
/*7*/   public async Task < int > FuncA()
/*8*/   {
/*9*/           Console.WriteLine("A--" + Thread.CurrentThread.ManagedThreadId);
/*10*/           var t = await FuncB();
/*11*/           Console.WriteLine("B--" + Thread.CurrentThread.ManagedThreadId);
/*12*/           return t;
/*13*/   }
/*14*/   
/*15*/   public async Task < int > FuncB()
/*16*/   {
/*17*/           Console.WriteLine("C--" + Thread.CurrentThread.ManagedThreadId);
/*18*/           await Task.Delay(2000);
/*19*/           Console.WriteLine("D--" + Thread.CurrentThread.ManagedThreadId);
/*20*/           return 999;
/*21*/   }
/*22*/   
/*23*/   void Main()
/*24*/   {
/*25*/           StartChain();
/*26*/   }
/*27*/   

結果是:

A--7
C--7
D--17         <-----D  and B are on different thread
B--17
999

那麼 Eric 所說的“留在目前執行緒上”是什麼意思?

編輯1:

其中asp.net還返回不同的執行緒 ID。

public async Task<int> FuncA()
{
   Response.Write("<br/>C----" + Thread.CurrentThread.ManagedThreadId);
   var t = await FuncB();
   Response.Write("<br/>D----" + Thread.CurrentThread.ManagedThreadId);
   return t;
}

public async Task<int> FuncB()
{
   Response.Write("<br/>E----" + Thread.CurrentThread.ManagedThreadId);
   await Task.Delay(2000);
   Response.Write("<br/>F----" + Thread.CurrentThread.ManagedThreadId);
   return 999;
}



protected async void Page_Load(object sender, EventArgs e)
{
   Response.Write("<br/>A----" + Thread.CurrentThread.ManagedThreadId);
   var a=await FuncA();
   Response.Write("<br/>B----" + Thread.CurrentThread.ManagedThreadId);

}

A----8
C----8
E----8
F----9
D----9
B----9

編輯 2

(得到答案後)

似乎該執行緒僅在 GUI 應用程序中提供:。我在winform上執行這段程式碼

 public async Task<int> FuncA()
       {
           textBox1.Text +=Environment.NewLine+ "\nC----" + Thread.CurrentThread.ManagedThreadId;
           var t = await FuncB();
           textBox1.Text += Environment.NewLine + "\nD----" + Thread.CurrentThread.ManagedThreadId;
           return t;
       }

       public async Task<int> FuncB()
       {
           textBox1.Text += Environment.NewLine + "\nE----" + Thread.CurrentThread.ManagedThreadId;
           await Task.Delay(2000);
           textBox1.Text += Environment.NewLine + "\nF----" + Thread.CurrentThread.ManagedThreadId;
           return 999;
       }




       private async void Form1_Load(object sender, EventArgs e)
       {
           textBox1.Text += Environment.NewLine + "\nA----" + Thread.CurrentThread.ManagedThreadId;
           var a = await FuncA();
           textBox1.Text += Environment.NewLine + "\nB----" + Thread.CurrentThread.ManagedThreadId;
       }

在此處輸入圖像描述

添加了 async/await 支持以幫助程序員編寫不會凍結的 GUI。在商店應用程序中特別有用,並且它被添加到 C# v5 的核心原因是,WinRT 是一個非常不友好的 api,它有許多非同步方法。

“保持在同一個執行緒上”場景在 GUI 應用程序中非常重要,這是必需的,因為 GUI 不是執行緒安全的。然而,它確實需要一個調度程序循環(又名 Application.Run),這是讓非同步程式碼在同一個執行緒上恢復的唯一方法。該循環是生產者-消費者問題的核心解決方案。

顯然你的程序沒有,看起來很像控制台模式應用程序。因此,您不會得到這種行為,它會在工作執行緒上恢復。

問題不大,您實際上並不需要它在同一個執行緒上恢復,因為無論如何控制台都是執行緒安全的。好吧,大多數情況下,當您要求輸入時,不包括在 .NET 4.5 中添加的鎖。當然,這也意味著您也沒有大量用於 async/await 的任務,Task 也可以正常工作。

如果我執行一個非同步方法並執行它,它肯定會在另一個執行緒上執行。

不,它通常在另一個執行緒上執行。它不一定在另一個執行緒上執行*。*

暫時停止思考執行緒,想想非同步的本質。非同步的本質是:

  • 我有一些我目前正在執行的工作流程。
  • 在獲得資訊 X 之前,我無法繼續此工作流程。
  • 在我得到資訊 X 之前,我會做其他事情。
  • 在未來的某個時候,一旦我有了 X,我將回到我在工作流程中中斷的地方並繼續。

假設您正在納稅,並且在這個複雜的工作流程中,您有大量的附加工作要執行。你可以執行一些操作然後記住你在哪裡,然後去吃午飯。然後回來再做幾個操作,然後記住你在哪裡,餵貓。然後回來再做幾個操作,然後記住你在哪裡,洗碗。然後完成計算,並從工作流程中斷的地方繼續。

這是一個非同步計算,但它只需要一個工作人員來完成。擁有多個工人只是一種特別方便的非同步方式,它不是必需的。

引用自:https://stackoverflow.com/questions/17661428