如何理解.NET 4并行編程中Task的取消,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
成都創(chuàng)新互聯(lián)公司從2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站制作、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元內(nèi)丘做網(wǎng)站,已為上家服務(wù),為內(nèi)丘各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108
因?yàn)門ask是.NET 4并行編程最為核心的一個(gè)類,也我們?cè)谑窃诓⑿芯幊坛34蚪坏赖念悾裕瑢?duì)Task對(duì)全面的了解很有必要。
小編主要講述如何取消一個(gè)task。
在TPL中一個(gè)標(biāo)準(zhǔn)化的操作就是”取消Task”。之所以說它是個(gè)標(biāo)準(zhǔn)化的操作,其實(shí)是把這個(gè)操作和之前傳統(tǒng)的多線程編程進(jìn)行比較而言的。
在之前的多線程編程中,我們一般是自己寫一些代碼來取消線程的運(yùn)行。但是在.NET 4的TPL中就內(nèi)置了取消的方法,可能我們覺得TPL沒有必要內(nèi)置這些代碼,因?yàn)樘?jiǎn)單了。但是這個(gè)內(nèi)置的方法不僅僅只是取消了運(yùn)行的Task,而且還減小了在取消運(yùn)行的Task時(shí)可能產(chǎn)生的一些風(fēng)險(xiǎn),我們后續(xù)文章會(huì)詳細(xì)講述。
創(chuàng)建一個(gè)取消的Task一般要進(jìn)行下面一些步驟:
a.創(chuàng)建System.Threading.CancellationTokenSource的一個(gè)實(shí)例:
// create the cancellation token source ancellationTokenSource tokenSource = new CancellationTokenSource();
b.通過CancellationTokenSource.Token屬性獲得一個(gè)System.Threading.CancellationToken:
CancellationToken token = tokenSource.Token;
c.創(chuàng)建一個(gè)新的Task或者Task
Task task = new Task(new Action(printMessage), token);
d.調(diào)用Task的Start()方法。
上面的步驟和我們之前介紹的創(chuàng)建一個(gè)Task的代碼幾乎一樣,只是在構(gòu)造函數(shù)中多傳入了一個(gè)參數(shù)。
如果想要取消一個(gè)Task的運(yùn)行,只要調(diào)用CancellationToken實(shí)例的Cancel()方法就可以了。
有點(diǎn)要特別注意的,當(dāng)我們調(diào)用了Cancel()方法之后,.NET Framework不會(huì)強(qiáng)制性的去關(guān)閉運(yùn)行的Task。
我們自己必須去檢測(cè)之前在創(chuàng)建Task時(shí)候傳入的那個(gè)CancellationToken。
我們?cè)趧?chuàng)建Task是傳入CancellationToken到構(gòu)造函數(shù),其實(shí)這個(gè)CancellationToken就是.NET Framework用來避免我們?cè)俅芜\(yùn)行已經(jīng)被取消的Task,可以說就是一個(gè)標(biāo)志位。
首先,進(jìn)入***個(gè)議題:
1.通過輪詢的方式檢測(cè)Task是否被取消
在很多Task內(nèi)部都包含了循環(huán),用來處理數(shù)據(jù)。我們可以在循環(huán)中通過CancellationToken的IsCancellationRequest屬性來檢測(cè)task是否被取消了。如果這個(gè)屬性為true,那么我們就得跳出循環(huán),并且釋放task所占用的資源(如數(shù)據(jù)庫資源,文件資源等).
我們也可以在task運(yùn)行體中拋出System.Threading.OperationCanceledException來取消運(yùn)行的task。
代碼如下:
代碼
while (true) { if (token.IsCancellationRequested) { // tidy up and release resources throw new OperationCanceledException(token); } else { // do a unit of work } }
如果我們沒有任何的資源要釋放,那么只要簡(jiǎn)單的調(diào)用CancellationToken.ThrowIfCancellationRequested()方法,這個(gè)方法會(huì)檢查是否要取消task,并且拋出異常。代碼如下:
while (true) token.ThrowIfCancellationRequested(); // do a unit of work }
下面就給出有一個(gè)完整的例子:創(chuàng)建一個(gè)可以取消的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來檢測(cè)Task是否被取消
我們可以在注冊(cè)一個(gè)委托到CancellationToken中,這個(gè)委托的方法在CancellationToken.Cancel()調(diào)用之前被調(diào)用。
我們可以用這個(gè)委托中的方法來作為一個(gè)檢測(cè)task是否被取消的另外一個(gè)可選的方法,因?yàn)檫@個(gè)方法是在Cancel()方法被調(diào)用之前就調(diào)用的,所以這個(gè)委托中的方法可以檢測(cè)task是否被cancel了,也就是說,只要這個(gè)委托的方法被調(diào)用,那么就說這個(gè)CancellationToken.Cancel()方法被調(diào)用了,而且在這個(gè)委托的方法中我們可以做很多的事情,如通知用戶取消操作發(fā)生了。
下面的代碼給出了一個(gè)例子。
代碼
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還檢測(cè)Task是否被取消
第三種方法檢測(cè)task是否被cancel就是調(diào)用CancellationToken.WaitHandle屬性。對(duì)于這個(gè)屬性的詳細(xì)使用,在后續(xù)的文章中會(huì)深入的講述,在這里主要知道一點(diǎn)就行了:CancellationToken的WaitOne()方法會(huì)阻止task的運(yùn)行,只有CancellationToken的cancel()方法被調(diào)用后,這種阻止才會(huì)釋放。
在下面的例子中,創(chuàng)建了兩個(gè)task,其中task2調(diào)用了WaitOne()方法,所以task2一直不會(huì)運(yùn)行,除非調(diào)用了CancellationToken的Cancel()方法,所以WaitOne()方法也算是檢測(cè)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. 取消多個(gè)Task
我們可以使用一個(gè)CancellationToken來創(chuàng)建多個(gè)不同的Tasks,當(dāng)這個(gè)CancellationToken的Cancel()方法調(diào)用的時(shí)候,使用了這個(gè)token的多個(gè)task都會(huì)被取消。
代碼
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. 創(chuàng)建組合的取消Task的Token
我們可以用CancellationTokenSource.CreateLinkedTokenSource()方法來創(chuàng)建一個(gè)組合的token,這個(gè)組合的token有很多的CancellationToken組成。主要組合token中的任意一個(gè)token調(diào)用了Cancel()方法,那么使用這個(gè)組合token的所有task就會(huì)被取消。代碼如下:
代碼
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. 判斷一個(gè)Task是否已經(jīng)被取消了
可以使用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(); }
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。