程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET 4並行(多核)編程系列之三 從Task的取消

.NET 4並行(多核)編程系列之三 從Task的取消

編輯:關於.NET

前言:因為Task是.NET 4並行編程最為核心的一個類,也我們在是在並行編程常常打交道的類,所以,對Task對全面的了解很有必要。

上篇文章主要講述了如何創建一個task,本篇文章主要講述如何取消一個task。

本篇主的主要議題如下:

1.通過輪詢的方式檢測Task是否被取消

2.用委托delegate來檢測Task是否被取消

3.用Wait Handle還檢測Task是否被取消

4.取消多個Task

5.創建組合的取消Task的Token

6.判斷一個Task是否已經被取消了

本篇的理論不多,代碼的例子很多。

在TPL中一個標准化的操作就是”取消Task”。之所以說它是個標准化的操作,其實是把這個操作和之前傳統的多線程編程進行比較而言的。

在之前的多線程編程中,我們一般是自己寫一些代碼來取消線程的運行。但是在.NET 4的TPL中就內置了取消的方法,可能我們覺得TPL沒有必要內置這些代碼,因為太簡單了。但是這個內置的方法不僅僅只是取消了運行的Task,而且還減小了在取消運行的Task時可能產生的一些風險,我們後續文章會詳細講述。

創建一個取消的Task一般要進行下面一些步驟:

a.a.創建System.Threading.CancellationTokenSource的一個實例:

// create the cancellation token source

CancellationTokenSource tokenSource = new CancellationTokenSource();

b.b.通過CancellationTokenSource.Token屬性獲得一個System.Threading.CancellationToken:

CancellationToken token = tokenSource.Token;

c.創建一個新的Task或者Task<T>,並且在構造函數傳入Action或者Action<object>的委托作為第一個參數,傳入CancellationToken作為第二個參數:

Task task = new Task(new Action(printMessage), token);

d.d.調用Task的Start()方法。

上面的步驟和我們之前介紹的創建一個Task的代碼幾乎一樣,只是在構造函數中多傳入了一個參數。

如果想要取消一個Task的運行,只要調用CancellationToken實例的Cancel()方法就可以了。

有點要特別注意的,當我們調用了Cancel()方法之後,.NET Framework不會強制性的去關閉運行的Task。

我們自己必須去檢測之前在創建Task時候傳入的那個CancellationToken。

我們在創建Task是傳入CancellationToken到構造函數,其實這個CancellationToken就是.NET Framework用來避免我們再次運行已經被取消的Task,可以說就是一個標志位。

首先,進入第一個議題:

1.通過輪詢的方式檢測Task是否被取消

在很多Task內部都包含了循環,用來處理數據。我們可以在循環中通過CancellationToken的IsCancellationRequest屬性來檢測task是否被取消了。如果這個屬性為true,那麼我們就得跳出循環,並且釋放task所占用的資源(如數據庫資源,文件資源等).

我們也可以在task運行體中拋出System.Threading.OperationCanceledException來取消運行的task。

代碼如下:

代碼

while (true)
{
     if (token.IsCancellationRequested)
     {
           // tidy up and release resources
            throw new OperationCanceledException(token);
     }
      else
      {
         // do a unit of work
        }
  }

如果我們沒有任何的資源要釋放,那麼只要簡單的調用CancellationToken.ThrowIfCancellationRequested()方法,這個方法會檢查是否要取消task,並且拋出異常。代碼如下:

while (true)
{
         token.ThrowIfCancellationRequested();
          // do a unit of work
  }

下面就給出有一個完整的例子:創建一個可以取消的task,並且通過輪詢不斷的檢查是否要取消task

代碼如下:

代碼

static void Main(string[] args)
         {
             // create the cancellation token source
             CancellationTokenSource tokenSource = new CancellationTokenSource();
             // create the cancellation token
             CancellationToken token = tokenSource.Token;
             // create the task
             Task task = new Task(() =>
             {
                 for (int i = 0; i < int.MaxValue; i++)
                 {
                     if (token.IsCancellationRequested)
                     {
                         Console.WriteLine("Task cancel detected");
                         throw new OperationCanceledException(token);
                     }
                     else
                     {
                         Console.WriteLine("Int value {0}", i);
                     }
                 }
             }, token);
             // wait for input before we start the task
             Console.WriteLine("Press enter to start task");
             Console.WriteLine("Press enter again to cancel task");
             Console.ReadLine();
             // start the task
             task.Start();
             // read a line from the console.
             Console.ReadLine();
             // cancel the task
             Console.WriteLine("Cancelling task");
             tokenSource.Cancel();
             // wait for input before exiting
             Console.WriteLine("Main method complete.Press enter to finish.");
             Console.ReadLine();
         }

2.用委托delegate來檢測Task是否被取消

我們可以在注冊一個委托到CancellationToken中,這個委托的方法在CancellationToken.Cancel()調用之前被調用。

我們可以用這個委托中的方法來作為一個檢測task是否被取消的另外一個可選的方法,因為這個方法是在Cancel()方法被調用之前就調用的,所以這個委托中的方法可以檢測task是否被cancel了,也就是說,只要這個委托的方法被調用,那麼就說這個CancellationToken.Cancel()方法被調用了,而且在這個委托的方法中我們可以做很多的事情,如通知用戶取消操作發生了。

下面的代碼給出了一個例子。

代碼

static void Main(string[] args)
         {
             // create the cancellation token source
             CancellationTokenSource tokenSource = new CancellationTokenSource();
             // create the cancellation token
             CancellationToken token = tokenSource.Token;
             // create the task
             Task task = new Task(() =>
             {
                 for (int i = 0; i < int.MaxValue; i++)
                 {
                     if (token.IsCancellationRequested)
                     {
                         Console.WriteLine("Task cancel detected");
                         throw new OperationCanceledException(token);
                     }
                     else
                     {
                         Console.WriteLine("Int value {0}", i);
                     }
                 }
             }, token);
             // register a cancellation delegate
             token.Register(() =>
             {
                 Console.WriteLine(">>>>>> Delegate Invoked\n");
             });
             // wait for input before we start the task
             Console.WriteLine("Press enter to start task");
             Console.WriteLine("Press enter again to cancel task");
             Console.ReadLine();
             // start the task
             task.Start();
             // read a line from the console.
             Console.ReadLine();
             // cancel the task
             Console.WriteLine("Cancelling task");
             tokenSource.Cancel();
             // wait for input before exiting
             Console.WriteLine("Main method complete.Press enter to finish.");
             Console.ReadLine();
         }

3.用Wait Handle還檢測Task是否被取消

第三種方法檢測task是否被cancel就是調用CancellationToken.WaitHandle屬性。對於這個屬性的詳細使用,在後續的文章中會深入的講述,在這裡主要知道一點就行了:CancellationToken的WaitOne()方法會阻止task的運行,只有CancellationToken的cancel()方法被調用後,這種阻止才會釋放。

在下面的例子中,創建了兩個task,其中task2調用了WaitOne()方法,所以task2一直不會運行,除非調用了CancellationToken的Cancel()方法,所以WaitOne()方法也算是檢測task是否被cancel的一種方法了。

代碼

static void Main(string[] args)
         {
             // create the cancellation token source
             CancellationTokenSource tokenSource = new CancellationTokenSource();
             // create the cancellation token
             CancellationToken token = tokenSource.Token;
             // create the task
             Task task1 = new Task(() =>
             {
                 for (int i = 0; i < int.MaxValue; i++)
                 {
                     if (token.IsCancellationRequested)
                     {
                         Console.WriteLine("Task cancel detected");
                         throw new OperationCanceledException(token);
                     }
                     else
                     {
                         Console.WriteLine("Int value {0}", i);
                     }
                 }
             }, token);
             // create a second task that will use the wait handle
             Task task2 = new Task(() =>
             {
                 // wait on the handle
                 token.WaitHandle.WaitOne();
                 // write out a message
                 Console.WriteLine(">>>>> Wait handle released");
             });
             // wait for input before we start the task
             Console.WriteLine("Press enter to start task");
             Console.WriteLine("Press enter again to cancel task");
             Console.ReadLine();
             // start the tasks
             task1.Start();
             task2.Start();
             // read a line from the console.
             Console.ReadLine();
             // cancel the task
             Console.WriteLine("Cancelling task");
             tokenSource.Cancel();
             // wait for input before exiting
             Console.WriteLine("Main method complete.Press enter to finish.");
             Console.ReadLine();
         }

4.取消多個Task

我們可以使用一個CancellationToken來創建多個不同的Tasks,當這個CancellationToken的Cancel()方法調用的時候,使用了這個token的多個task都會被取消。

代碼

static void Main(string[] args)
         {
             // create the cancellation token source
             CancellationTokenSource tokenSource = new CancellationTokenSource();
             // create the cancellation token
             CancellationToken token = tokenSource.Token;
             // create the tasks
             Task task1 = new Task(() =>
             {
                 for (int i = 0; i < int.MaxValue; i++)
                 {
                     token.ThrowIfCancellationRequested();
                     Console.WriteLine("Task 1 - Int value {0}", i);
                 }
             }, token);
             Task task2 = new Task(() =>
             {
                 for (int i = 0; i < int.MaxValue; i++)
                 {
                     token.ThrowIfCancellationRequested();
                     Console.WriteLine("Task 2 - Int value {0}", i);
                 }
             }, token);
             // wait for input before we start the tasks
             Console.WriteLine("Press enter to start tasks");
             Console.WriteLine("Press enter again to cancel tasks");
             Console.ReadLine();
             // start the tasks
             task1.Start();
             task2.Start();
             // read a line from the console.
             Console.ReadLine();
             // cancel the task
             Console.WriteLine("Cancelling tasks");
             tokenSource.Cancel();
             // wait for input before exiting
             Console.WriteLine("Main method complete.Press enter to finish.");
             Console.ReadLine();
         }

5.創建組合的取消Task的Token

我們可以用CancellationTokenSource.CreateLinkedTokenSource()方法來創建一個組合的token,這個組合的token有很多的CancellationToken組成。主要組合token中的任意一個token調用了Cancel()方法,那麼使用這個組合token的所有task就會被取消。代碼如下:

代碼

static void Main(string[] args)
         {
             // create the cancellation token sources
             CancellationTokenSource tokenSource1 = new CancellationTokenSource();
             CancellationTokenSource tokenSource2 = new CancellationTokenSource();
             CancellationTokenSource tokenSource3 = new CancellationTokenSource();
             // create a composite token source using multiple tokens
             CancellationTokenSource compositeSource = 
                 CancellationTokenSource.CreateLinkedTokenSource(
             tokenSource1.Token, tokenSource2.Token, tokenSource3.Token);
             // create a cancellable task using the composite token
             Task task = new Task(() =>
             {
                 // wait until the token has been cancelled
                 compositeSource.Token.WaitHandle.WaitOne();
                 // throw a cancellation exception
                 throw new OperationCanceledException(compositeSource.Token);
             }, compositeSource.Token);
             // start the task
             task.Start();
             // cancel one of the original tokens
             tokenSource2.Cancel();
             // wait for input before exiting
             Console.WriteLine("Main method complete.Press enter to finish.");
             Console.ReadLine();
         }

6.判斷一個Task是否已經被取消了

可以使用Task的IsCancelled屬性來判斷task是否被取消了。代碼如下:

代碼

static void Main(string[] args)
         {
             // create the cancellation token source
             CancellationTokenSource tokenSource1 = new CancellationTokenSource();
             // create the cancellation token
             CancellationToken token1 = tokenSource1.Token;
             // create the first task, which we will let run fully
             Task task1 = new Task(() =>
             {
                 for (int i = 0; i < 10; i++)
                 {
                     token1.ThrowIfCancellationRequested();
                     Console.WriteLine("Task 1 - Int value {0}", i);
                 }
             }, token1);
             // create the second cancellation token source
             CancellationTokenSource tokenSource2 = new CancellationTokenSource();
             // create the cancellation token
             CancellationToken token2 = tokenSource2.Token;
             // create the second task, which we will cancel
             Task task2 = new Task(() =>
             {
                 for (int i = 0; i < int.MaxValue; i++)
                 {
                     token2.ThrowIfCancellationRequested();
                     Console.WriteLine("Task 2 - Int value {0}", i);
                 }
             }, token2);
             // start all of the tasks
             task1.Start();
             task2.Start();
             // cancel the second token source
             tokenSource2.Cancel();
             // write out the cancellation detail of each task
             Console.WriteLine("Task 1 cancelled? {0}", task1.IsCanceled);
             Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);
             // wait for input before exiting
             Console.WriteLine("Main method complete.Press enter to finish.");
             Console.ReadLine();
         }

今天就寫到這裡,比較的簡單,都是一些很基礎的東西,只有這樣,後面深入講解的時候才更加的順利。

謝謝各位!

出處:http://www.cnblogs.com/yanyangtian

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