C#多線程編程(3)--開啟子任務


上一篇我講解了await和async關鍵字,這兩個關鍵字的作用是將async限定的方法中await關鍵字后面的部分封裝成一個委托,該委托會在await修飾的Task完成后再執行。簡單的說,就是等待任務完成后,后面的程序才執行,且該等待不會造成線程阻塞。關鍵是在任務執行完成后,程序會繼續交給主線程執行。接下來,我來介紹在任務執行結束后,用新任務來執行方法。

 

廢話不多上,上代碼,我們來看看如何在任務結束后繼續由線程池繼續完成其他方法。

 1 static void Main(string[] args)
 2 {
 3     RunAsync();
 4     Console.WriteLine("Async Run");
 5     Console.Read();
 6 }
 7 public static void RunAsync()
 8 {
 9     var task = Task.Run(() =>
10     {
11         Thread.Sleep(2000);
12         return "task finished";
13     });
14     //當task完成后,會在由線程池繼續執行
15     task.ContinueWith(t =>
16     {
17         Thread.Sleep(2000);
18         Console.WriteLine(task.Result);
19     });
20 }

可以看到,RunAsync方法中並沒有添加async和await關鍵字,但是運行程序后,主線程沒有被阻塞。這是因為ContinueWith會將委托參數“掛”到線程池的任務隊列中,該委托只有在task執行結束后,才會開始執行。ContinueWith方法會返回一個Task,該Task就是task執行結束后會開始執行的Task,該Task也是可以等待的。注意,ContinueWIth和await關鍵字的區別就在:await在任務完成后,會由主線程繼續執行,但是ContinueWith中的方法會繼續由線程池執行。

接下來,我們來了解一下,任務能否取消,以及取消后會發生什么。

C#提供的任務取消的方式是“協作式”取消,在構造任務時,需要先構造一個System.Threading.CancellationTaskSource對象,該對象看起來像這樣

1  public sealed class CancellationTaskSource:IDisposable{
2     public CancellationTokenSource();
3     public void Dispose();//釋放資源
4     public bool IsCancellationRequested{get;}
5     public CancellationToken Token{get;}  
6     public void Cancel();//內部調用Cancel並傳遞false
7     public void Cancel(bool throwOnFirstException);
8 }

CancellationTaskSource對象中含有一個Token,它的類型是CancellationToken,這是一個輕量級值類型,它包含一個私有的對CancellationTaskSource的引用,當構造Task時,需要將這個Token傳入,如下所示

static void Main(string[] args){
    CancelTask();
    Console.WriteLine("Async Run");
    Console.Read();
}
public static void CancelTask()
{
    //定時1000ms后自動取消任務
    var cancelSource = new CancellationTokenSource(1000);
    Task.Run(() =>
    {
        //模擬其他任務
        Thread.Sleep(4000);
        //如果取消請求已發送,則不會顯示Task over
        if (!cancelSource.IsCancellationRequested)
            Console.WriteLine("Task over");
    }, cancelSource.Token);
    cancelSource.Token.Register(() => Console.WriteLine("Task cancel"));
}

 

運行后可以看到大約1秒后,控制台顯示了Task cancel,並且在沒有顯示Task over。證明任務被取消了。Task.Run()方法的其中一個重載是接受一個CancelToken參數,我們傳入的是cancelSource的Token,這表明cancelSource就可以控制Task的取消與否。我的做法是在1秒后自動取消任務,也可以手動調用cancelSource.Cancel()方法來顯式取消任務。我在Token上訂閱了取消的事件,在任務被取消后,執行我的方法,這里是控制台打印出Task cancel。也可以在任務中,利用“閉包”,將Token傳入到任務中,如下

 1 static void Main(string[] args){
 2     CancelTask();
 3     Console.WriteLine("Async Run");
 4     Console.Read();
 5 }
 6 public static void CancelTask(){
 7     var cancelSource = new CancellationTokenSource(1000);
 8     Task.Run(() =>{
 9         int count = 0;
10         for (int i = 0; i < 1000; i++)
11         {
12             if (cancelSource.IsCancellationRequested)
13                 break;
14             count+=i;
15             Thread.Sleep(30);
16         }
17         Console.WriteLine(count);
18     }, cancelSource.Token);
19     cancelSource.Token.Register(() => Console.WriteLine("Task cancel"));
20 }

手動取消有2個方法,一是cancelSource.Cancel(),另一個是cancelSource.CancelAfter(),示例如下

 1 public static void CancelTask(){
 2     var cancelSource = new CancellationTokenSource();
 3     Task.Run(() =>
 4     {
 5         Thread.Sleep(4000);
 6         Console.WriteLine("Task over");
 7     }, cancelSource.Token);
 8     cancelSource.Token.Register(() => Console.WriteLine("Task cancel"));
 9     //此處調用cancelSource.Cancel()的話會立即結束任務
10     //該方法會大約1秒后取消任務
11     cancelSource.CancelAfter(1000);
12 }

該示例和上面的在構造函數中傳入1000ms的延時是一樣的效果,若調用Cancel(),則立刻結束任務。

以上就是本篇內容,介紹了Task.ContinueWith方法,以及如何取消任務。若文中不能滿足你的要求,可以查看諸如Task.Run()或者Task.ContinueWith()方法的幾個重載,還有其他的使用方法,本人並沒有將全部細枝末節全部學會,主要的目的是掌握多線程的基本使用方法。《CLR via C#》書中也有很詳細的講解,我的這個系列就是從該書中提取,有興趣的小伙伴可以看一看。歡迎有問題的和我在評論區交流。

下一篇,我給大家介紹一下C#並行的奇技淫巧:並行的For、Foreach循環以及PLINQ。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM