天天看點

C#多線程程式設計筆記(5.4)-對并行執行的異步任務使用await操作符

近來在學習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);
        }
    }
}
           

程式運作結果如下:

C#多線程程式設計筆記(5.4)-對并行執行的異步任務使用await操作符

這裡我們定義了兩個異步任務,分别運作3秒和5秒。然後使用Task.WhenAll輔助方法建立了另一個任務,該任務隻有在底層任務完成後才會運作。之後我們等待該組合任務的結果。5秒後,我們擷取了所有結果,說明了這些任務是同時運作的。

然而這裡觀察到一個有意思的現象。當運作該程式時,我們注意到這兩個任務似乎是被線程池中的同一個工作線程執行的。當我們并行運作任務時怎麼可能發生這樣的事情呢?為了讓事情更有趣,我們來注釋掉GetInfoAsync方法中的await Task.Delay代碼行,并解除對await Task.Run代碼行的注釋,然後再次運作程式。

C#多線程程式設計筆記(5.4)-對并行執行的異步任務使用await操作符

我們看到該情況下兩個任務被不同的工作線程執行。不同之處是Task.Delay在幕後使用了一個計時器,過程如下:從線程池中擷取工作線程,它将等待Task.Delay方法傳回結果。然後,Task.Delay方法啟動計時器并指定一塊代碼,該代碼會再計時器時間到了Task.Delay方法中指定的秒數後被調用。之後立即将工作線程傳回到線程池中。當計時器事件運作時,我們又從線程池中任意擷取一個可用的工作線程(可能就是運作一個任務時使用的線程)并運作計時器提供給它的代碼。

當使用Task.Run方法時,從線程池中擷取了一個工作線程并将其阻塞幾秒,具體秒數由Thread.Sleep方法提供。然後擷取了第二個工作線程并且也将其阻塞。在這種場景下,我們消費了兩個工作線程,而它們絕對什麼事沒做,因為在它們等待時不能執行任何其他操作。

盡可能地使用第一種方式,是建立高伸縮性的伺服器程式的關鍵!