這節講一下比較進階的異步程式設計用法Task,以及兩個異步關鍵字async和await。
Task是在C#5.0推出的文法,它是基于任務的異步程式設計文法,是對Thread的更新,也提供了很多API,先看一下Task怎麼使用:
System.Threading.Tasks.Task.Run(() =>
{
Console.WriteLine("異步");
});
System.Threading.Tasks.Task aTask=new System.Threading.Tasks.Task(() =>
{
Console.WriteLine("異步");
});
aTask.Start();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
Console.WriteLine("異步");
});
Task.Run()可以直接異步運作一個方法,或者使用執行個體化Task傳入委托的方式,通過start()進行啟動,再或者使用Task.Factory.StartNew()直接啟動。
async,await
為了進一步介紹Task,需要先介紹兩個異步有關的關鍵字async,await
async用在方法的聲明,await用于代碼語句中。被async标記的方法,稱作異步方法。但是,并非整個方法都是異步執行,代碼中以await開頭标記的代碼,才是要真正異步執行的具體内容。這個關鍵字一般是配合Task來使用的,Task有泛型的形式,辨別異步的傳回值類型,通過Result()方法擷取傳回值。這段說明很難了解,下面看代碼示範:
static void Main(string[] args)
{
Console.WriteLine("建立一個異步任務");
Task<int> task = new Program().GetValue();
Console.WriteLine("正在計算結果....");
Console.WriteLine($"運作結果為:{task.Result}");
Console.WriteLine("任務完成....");
}
public async Task<int> GetValue()
{
Console.WriteLine("即将開始進行計算...3");
Console.WriteLine("即将開始進行計算...2");
Console.WriteLine("即将開始進行計算...1");
int a= await System.Threading.Tasks.Task.Run(() =>
{
int i = 0;
for (; i < 10; i++)
{
Console.WriteLine(i);
}
return i;
});
Console.WriteLine("結果計算完成....");
return a;
}
運作結果為:
從運作結果可以看出,程式運作到15行await處後,下一步就跳出了這個方法,回到第6行執行,這也是await的一個特性,異步執行,将主線程執行權交回,也就是說,從15行到25行是在背景線程中執行的,之前的執行都是同步的,之後的執行也是同步的,而且,主線程的腳步沒有停下,直到遇到task.Result,Result裡邊存放着異步方法運作的傳回值,運作到這,如果異步沒有完成,就會阻塞目前線程,直到異步傳回結果。
另外說一點,之前在講自定義中間件的時候,涉及到過這兩個關鍵詞,現在明白了這個用法,可以回去再看一下,應該會對中間件的通路流程有一個更清晰的了解。
ContinueWith
ContinueWith設定Task在執行完原有任務後,再繼續執行此方法設定的方法,下面看代碼:
task.ContinueWith((task) =>
{
Console.WriteLine("---------------"+a.Result);
});
這是其中的一個重載,接受一個Action<Task<T>>類型的委托,此處乍一看可能會不解,其實就是把目前執行任務的Task對象傳進來了。這樣的用法有什麼好處呢,運作完了以後,可以直接取Task任務的傳回值,不用阻塞線程,當然這是在傳回值不是急需的情況下。
CancellationTokenSource
CancellationTokenSource類用于終止一個任務,請先看一下代碼:
static void Main(string[] args)
{
CancellationTokenSource cancellationToken=new CancellationTokenSource();
Task<int> task= new Program().GetValue(cancellationToken);
cancellationToken.Cancel();
Console.WriteLine("正在計算結果....");
Console.WriteLine($"運作結果為:{task.Result}");
Thread.Sleep(1000);
Console.WriteLine("任務完成....");
}
public Task<int> GetValue(CancellationTokenSource cancellationToken)
{
Task<int> a = System.Threading.Tasks.Task.Run(() =>
{
int i = 0;
for (; i < 100; i++)
{
Console.WriteLine(i);
Thread.Sleep(500);
}
return i;
},cancellationToken.Token);
Console.WriteLine("結果計算完成....");
return a;
}
建立一個CancellationTokenSource對象,在Run任務的時候傳入一個Token,就能調用Cancel()方法就能終止這個任務,運作結果為:
可以看到報錯了,這很正常,因為任務停止了,顯然Result是沒有值的
最後注意一點,異步不是多線程,可以說異步是基于多線程,但是它們不是等于的關系。