程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> await之後的線程問題,await線程問題

await之後的線程問題,await線程問題

編輯:C#入門知識

await之後的線程問題,await線程問題


之前看了園子裡的一篇文章「async & await的前世今生」,收益頗多。而其中有句話被博主特意用紅色標注,所以留意多看了幾眼,「await 之後不會開啟新的線程(await 從來不會開啟新的線程)」。在MSDN上找到的相關資料也佐證了其正確性——The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.(async 和 await 關鍵字不會導致創建其他線程。 因為異步方法不會在其自身線程上運行,因此它不需要多線程。 只有當方法處於活動狀態時,該方法將在當前同步上下文中運行並使用線程上的時間。)

再建立一個Windows Forms應用工程,寫點代碼更形象地說明問題:

private void Form1_Load(object sender, EventArgs e)
{
    PrintDataAsync();
    Debug.Print("three");
}

private async void PrintDataAsync()
{
    Task<int> result = CalculateDataAsync();
    Debug.Print("second");
    int data = await result;
    Debug.Print("last:" + data);
}

private async Task<int> CalculateDataAsync()
{
    Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    Debug.Print("first");
    int result = 0;
    for (int i = 0; i < 10; i++)
    {
        result += i;
    }
    await Task.Delay(1000);
    Debug.Print("four");
    Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    return result;
};

程序的結果如預期的一樣,Output窗口中可以看到以下內容:

8 : False
first
second
three
four
8 : False
last:45

await之前的ManagedThreadId值與之後的ManagedThreadId值一致,IsThreadPoolThread始終是False,說明當前線程沒有發生改變,也沒有產生新的線程。

但如果建立的是Console應用工程,結果就不同了。

static void Main(string[] args)
{
    PrintDataAsync();
    Console.WriteLine("three");
    Console.Read();
}

private static async void PrintDataAsync()
{
    Task<int> result = CalculateDataAsync();
    Console.WriteLine("second");
    int data = await result;
    Console.WriteLine("last:" + data);
}

private static async Task<int> CalculateDataAsync()
{
    Console.WriteLine(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    Console.WriteLine("first");
    int result = 0;
    for (int i = 0; i < 10; i++)
    {
        result += i;
    }
    await Task.Delay(1000);
    Console.WriteLine("four");
    Console.WriteLine(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    return result;
}

這段代碼的執行結果:

8 : False
first
second
three
four
10 : True
last:45

ManagedThreadId在await之後發生了變化,IsThreadPoolThread也變為了True,說明不是同一個線程。

為什麼會這樣?再看一下MSDN中描述——「The method runs on the current synchronization context and uses time on the thread only when the method is active」,這裡涉及到SynchronizationContext對象的使用。

在Windows Forms工程代碼中加入 Debug.Print(SynchronizationContext.Current.ToString()); 檢測代碼,其輸出是System.Windows.Forms.WindowsFormsSynchronizationContext。

而如果在Console工程中加入類似的檢測代碼 Console.WriteLine(SynchronizationContext.Current.ToString()); 則會拋出空引用異常,因為SynchronizationContext.Current在Console工程中的值為null。

又從MSDN Magazine找到SynchronizationContext相關的文章,其中有介紹到:By convention, if a thread’s current SynchronizationContext is null, then it implicitly has a default SynchronizationContext.(根據慣例,如果一個線程的當前 SynchronizationContext 為 null,那麼它隱式具有一個默認 SynchronizationContext。)The default SynchronizationContext is applied to ThreadPool threads unless the code is hosted by ASP.NET.(默認 SynchronizationContext 應用於 ThreadPool 線程,除非代碼由 ASP.NET 承載。)

這裡提到了APS.NET,所以再建個Web Forms應用工程用於驗證:

protected void Page_Load(object sender, EventArgs e)
{
    PrintDataAsync();
    Debug.Print("three");
}

private async void PrintDataAsync()
{
    Debug.Print(SynchronizationContext.Current.ToString());
    Task<int> result = CalculateDataAsync();
    Debug.Print("second");
    int data = await result;
    Debug.Print("last:" + data);
}

private async Task<int> CalculateDataAsync()
{
    Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    Debug.Print("first");
    int result = 0;
    for (int i = 0; i < 10; i++)
    {
        result += i;
    }
    await Task.Delay(1000);
    Debug.Print("four");
    Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    return result;
}

輸出結果:

System.Web.AspNetSynchronizationContext
8 : True
first
second
three
four
9 : True
last:45

ManagedThreadId值發生改變,IsThreadPoolThread始終是True,SynchronizationContext.Current值為System.Web.AspNetSynchronizationContext。

由三次試驗及相關資料可以得出結論,await之後的線程依據SynchronizationContext在不同環境中的不同定義而產生不同的結果。所以「await 之後不會開啟新的線程(await 從來不會開啟新的線程)」的肯定句式改成「await 之後會開啟新的線程嗎? Maybe」這樣的句式更加合適些。

最後補充一點,若是把第一個Windows Forms工程的代碼 await Task.Delay(1000); 改成 await Task.Delay(1000).ConfigureAwait(false); 的話,則可以得到第二個Console工程同樣的結果。

 


,java多線程中,await與wait的具體不同在哪?

其實await和wait的用法差不多。await是wait改進過來,現在開發一多半都用await,因為await加入了lock方法
Lock 替代了 synchronized 方法和語句的使用
 

[新手助] c# 異步編程,用了await了,為何還是頁面卡頓?

是因為這些操作還是被派給了UI線程來執行。
await可以被理解成.NET 4中的CallBack:當任務結束後會觸發一個完成事件。使用await的目的是await標記的代碼在完成事件被觸發以後再執行。應該不會影響到UI的卡與不卡。
試著把LetFeedShowAsync(int index)被另一線程調用,看看能否解決你的問題。
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved