TPL异步并行编程之简单使用
在上篇随笔里面说明了Task的使用,仅仅是简单使用,有时候把一个任务交给Task去执行,但是呢还是要管理下,比如说:我要叫这个任务停止了,不做了,任务取消了,或者超时了
在传统的While里面我们可以这样做,1 通过标识 2 通过一个方法抛异常,3 其他办法
举个例子:while(true){
if(isNotCancel){
//每次都判断下,取消没有,当然isNotCancel需要加上lock的
}
}
难道在Task里面有什么新奇吗?其实也没啥新奇的,那Task怎么取消呢?来点干货吧
一 轮训检测
直接调用task.cancel方法,但是下Task函数体内部必须做轮训检测是否被取消,且看代码
1 static void Main(string[] args) 2 { 3 CancellationTokenSource tokenSource = new CancellationTokenSource(); 4 5 CancellationToken token = tokenSource.Token; 6 7 Task task=new Task(() => 8 { 9 while (true) 10 { 11 if (token.IsCancellationRequested) 12 { 13 break; 14 } 15 else 16 { 17 18 } 19 } 20 },token); 21 22 23 Console.WriteLine("运行"); 24 25 26 27 task.Start(); 28 29 Thread.Sleep(5*1000); 30 31 tokenSource.Cancel(); 32 33 Console.ReadKey(); 34 }
二 取消过程拦截检测
在调用task.cancel方法是会在真正改变token.IsCancellationRequested值之前,调用token中所注册的函数,也就是说token.cancel(),调用后会调用token.register所注册的方法,然后再更改token.IsCancellationRequested只为true,那么反过来在注册的方法中我们就可以检测是否调用了cancel方法了也就自然检测到了已经取消了
且看代码说话:
1 static void Main(string[] args) 2 { 3 CancellationTokenSource tokenSource = new CancellationTokenSource(); 4 5 CancellationToken token = tokenSource.Token; 6 7 Task task=new Task(() => 8 { 9 while (true) 10 { 11 if (token.IsCancellationRequested) 12 { 13 break; 14 } 15 else 16 { 17 18 } 19 } 20 },token); 21 22 23 Console.WriteLine("运行"); 24 25 token.Register(() => 26 { 27 Console.WriteLine("取消了,在取消之前必定调用了我"); 28 }); 29 30 task.Start(); 31 32 Thread.Sleep(5*1000); 33 34 tokenSource.Cancel(); 35 36 Console.ReadKey(); 37 }
三 使用信号量来检测是否取消
现在我们启用2个Task,TaskA,TaskB,TaskB需要TaskA取消后才能执行,那么我们也可以在TaskB中执行代码时检测TaskA已被取消了,且看代码
1 static void Main(string[] args) 2 { 3 CancellationTokenSource tokenSource = new CancellationTokenSource(); 4 5 CancellationToken token = tokenSource.Token; 6 7 Task task=new Task(() => 8 { 9 while (true) 10 { 11 // 一直在运行,下面那个家伙得等着我被取消或者把事情做完 12 if (token.IsCancellationRequested) 13 { 14 //我已被取消该时候退出了 15 break; 16 } 17 } 18 },token); 19 20 21 Task.Run(() => 22 { 23 //我一开始就被上面那个task家伙挂起了,我需要他取消我才能干活~~ 24 token.WaitHandle.WaitOne(); 25 while (true) 26 { 27 // 开始干活 28 } 29 }); 30 31 task.Start(); 32 33 Thread.Sleep(5*1000); 34 35 tokenSource.Cancel(); 36 37 Console.ReadKey(); 38 }
四 多个协作的Task一个取消则其他Task也被取消,这样也可以取消一组Task
就好比我们几个人一起干一件事情,但是这件事情需要每个分工的相互协作才能继续,比如玉女双休剑,需要2人同时练功才行,其中一个人说我不行了 那都不行了,且看代码
4.1 共用一个Token
1 static void Main(string[] args) 2 { 3 CancellationTokenSource tokenSource = new CancellationTokenSource(); 4 5 // create the cancellation token 6 CancellationToken token = tokenSource.Token; 7 8 // create the tasks 9 Task task1 = new Task(() => 10 { 11 for (int i = 0; i < int.MaxValue; i++) 12 { 13 token.ThrowIfCancellationRequested(); 14 Console.WriteLine("Task 1 - Int value {0}", i); 15 } 16 }, token); 17 18 Task task2 = new Task(() => 19 { 20 for (int i = 0; i < int.MaxValue; i++) 21 { 22 token.ThrowIfCancellationRequested(); 23 Console.WriteLine("Task 2 - Int value {0}", i); 24 } 25 }, token); 26 // wait for input before we start the tasks 27 Console.WriteLine("Press enter to start tasks"); 28 Console.WriteLine("Press enter again to cancel tasks"); 29 Console.ReadLine(); 30 31 // start the tasks 32 task1.Start(); 33 task2.Start(); 34 35 // read a line from the console. 36 Console.ReadLine(); 37 38 // cancel the task 39 Console.WriteLine("Cancelling tasks"); 40 tokenSource.Cancel(); 41 // wait for input before exiting 42 Console.WriteLine("Main method complete. Press enter to finish."); 43 Console.ReadLine(); 44 }
4.2 Token组
1 static void Main(string[] args) 2 { 3 // create the cancellation token sources 4 CancellationTokenSource tokenSource1 = new CancellationTokenSource(); 5 CancellationTokenSource tokenSource2 = new CancellationTokenSource(); 6 CancellationTokenSource tokenSource3 = new CancellationTokenSource(); 7 8 // create a composite token source using multiple tokens 9 CancellationTokenSource compositeSource = 10 CancellationTokenSource.CreateLinkedTokenSource( 11 tokenSource1.Token, tokenSource2.Token, tokenSource3.Token); 12 13 // create a cancellable task using the composite token 14 Task task = new Task(() => 15 { 16 // wait until the token has been cancelled 17 compositeSource.Token.WaitHandle.WaitOne(); 18 // throw a cancellation exception 19 throw new OperationCanceledException(compositeSource.Token); 20 }, compositeSource.Token); 21 22 // start the task 23 task.Start(); 24 25 // cancel one of the original tokens 26 Thread.Sleep(2*1000); 27 tokenSource2.Cancel(); 28 29 // wait for input before exiting 30 Console.WriteLine("Main method complete. Press enter to finish."); 31 Console.ReadLine(); 32 }
五 抛出异常
ThrowIfCancellationRequested,在四中已经看到如果调用cancel方法会处罚ThrowIfCancellationRequested函数的执行,那么相应的Task检测到异常如果不做任何处理的情况下也就退出了,且看代码
1 代码 2 3 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> static void Main(string[] args) 4 { 5 // create the cancellation token source 6 CancellationTokenSource tokenSource1 = new CancellationTokenSource(); 7 8 // create the cancellation token 9 CancellationToken token1 = tokenSource1.Token; 10 11 // create the first task, which we will let run fully 12 Task task1 = new Task(() => 13 { 14 for (int i = 0; i < 10; i++) 15 { 16 token1.ThrowIfCancellationRequested(); 17 Console.WriteLine("Task 1 - Int value {0}", i); 18 } 19 }, token1); 20 21 // create the second cancellation token source 22 CancellationTokenSource tokenSource2 = new CancellationTokenSource(); 23 24 // create the cancellation token 25 CancellationToken token2 = tokenSource2.Token; 26 27 // create the second task, which we will cancel 28 Task task2 = new Task(() => 29 { 30 for (int i = 0; i < int.MaxValue; i++) 31 { 32 token2.ThrowIfCancellationRequested(); 33 Console.WriteLine("Task 2 - Int value {0}", i); 34 } 35 }, token2); 36 37 // start all of the tasks 38 task1.Start(); 39 task2.Start(); 40 41 // cancel the second token source 42 tokenSource2.Cancel(); 43 // write out the cancellation detail of each task 44 Console.WriteLine("Task 1 cancelled? {0}", task1.IsCanceled); 45 Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled); 46 // wait for input before exiting 47 Console.WriteLine("Main method complete. Press enter to finish."); 48 Console.ReadLine(); 49 }
最后
其实取消Task的执行还有其他办法,也可以自己实现不一定就要TPL通过的api来实现