近來在學習Eugene Agafonov編寫的《C#多線程程式設計實戰》(譯),做些筆記也順便分享一下^-^
本篇将學習如何使用await來并行地運作異步任務,而不是采用常用的順序執行。
using System;
using System.Threading.Tasks;
using System.Threading;
namespace 對并行執行的異步任務使用await操作符
{
class Program
{
static void Main(string[] args)
{
Task t = AsynchronousProcessing();
t.Wait();
Console.ReadKey();
}
async static Task AsynchronousProcessing()
{
Task<string> t1 = GetInfoAsync("Task 1", 3);
Task<string> t2 = GetInfoAsync("Task 2", 5);
string[] results = await Task.WhenAll(t1, t2);
foreach (string result in results)
{
Console.WriteLine(result);
}
}
async static Task<string> GetInfoAsync(string name,int seconds)
{
await Task.Delay(TimeSpan.FromSeconds(seconds));
//await Task.Run(() => Thread.Sleep(TimeSpan.FromSeconds(seconds)));
return string.Format("Task {0} is running on a thread id {1}.Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
}
}
}
程式運作結果如下:
這裡我們定義了兩個異步任務,分别運作3秒和5秒。然後使用Task.WhenAll輔助方法建立了另一個任務,該任務隻有在底層任務完成後才會運作。之後我們等待該組合任務的結果。5秒後,我們擷取了所有結果,說明了這些任務是同時運作的。
然而這裡觀察到一個有意思的現象。當運作該程式時,我們注意到這兩個任務似乎是被線程池中的同一個工作線程執行的。當我們并行運作任務時怎麼可能發生這樣的事情呢?為了讓事情更有趣,我們來注釋掉GetInfoAsync方法中的await Task.Delay代碼行,并解除對await Task.Run代碼行的注釋,然後再次運作程式。
我們看到該情況下兩個任務被不同的工作線程執行。不同之處是Task.Delay在幕後使用了一個計時器,過程如下:從線程池中擷取工作線程,它将等待Task.Delay方法傳回結果。然後,Task.Delay方法啟動計時器并指定一塊代碼,該代碼會再計時器時間到了Task.Delay方法中指定的秒數後被調用。之後立即将工作線程傳回到線程池中。當計時器事件運作時,我們又從線程池中任意擷取一個可用的工作線程(可能就是運作一個任務時使用的線程)并運作計時器提供給它的代碼。
當使用Task.Run方法時,從線程池中擷取了一個工作線程并将其阻塞幾秒,具體秒數由Thread.Sleep方法提供。然後擷取了第二個工作線程并且也将其阻塞。在這種場景下,我們消費了兩個工作線程,而它們絕對什麼事沒做,因為在它們等待時不能執行任何其他操作。
盡可能地使用第一種方式,是建立高伸縮性的伺服器程式的關鍵!