.NET 4 并行(多核)编程系列之三 从Task的取消
原文:.NET 4 并行(多核)编程系列之三 从Task的取消
.NET 4 并行(多核)编程系列之三 从Task的取消
前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程常常打交道的类,所以,对Task对全面的了解很有必要。
上篇文章主要讲述了如何创建一个task,本篇文章主要讲述如何取消一个task。
本篇主的主要议题如下:
1. 1. 通过轮询的方式检测Task是否被取消
2. 2. 用委托delegate来检测Task是否被取消
3. 3. 用Wait Handle还检测Task是否被取消
4. 4. 取消多个Task
5. 5. 创建组合的取消Task的Token
6. 6. 判断一个Task是否已经被取消了
系列文章链接:
本篇的理论不多,代码的例子很多。
在TPL中一个标准化的操作就是”取消Task”。之所以说它是个标准化的操作,其实是把这个操作和之前传统的多线程编程进行比较而言的。
在之前的多线程编程中,我们一般是自己写一些代码来取消线程的运行。但是在.NET 4的TPL中就内置了取消的方法,可能我们觉得TPL没有必要内置这些代码,因为太简单了。但是这个内置的方法不仅仅只是取消了运行的Task,而且还减小了在取消运行的Task时可能产生的一些风险,我们后续文章会详细讲述。
创建一个取消的Task一般要进行下面一些步骤:
a. a.创建System.Threading.CancellationTokenSource的一个实例:
// create the cancellation token sourceCancellationTokenSource 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。
代码如下:
代码
{
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
代码如下:
代码
{
// 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 = ; 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()方法被调用了,而且在这个委托的方法中我们可以做很多的事情,如通知用户取消操作发生了。
下面的代码给出了一个例子。
代码
{
// 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 = ; 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的一种方法了。
代码
{
// 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 = ; 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都会被取消。
代码
{
// 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 = ; i < int.MaxValue; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Task 1 - Int value {0}", i);
}
}, token);
Task task2 = new Task(() =>
{
for (int i = ; 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就会被取消。代码如下:
代码
{
// 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是否被取消了。代码如下:
代码
{
// 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 = ; i < ; 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 = ; 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
.NET 4 并行(多核)编程系列之三 从Task的取消的更多相关文章
- .NET 并行(多核)编程系列之七 共享数据问题和解决概述
原文:.NET 并行(多核)编程系列之七 共享数据问题和解决概述 .NET 并行(多核)编程系列之七 共享数据问题和解决概述 前言:之前的文章介绍了了并行编程的一些基础的知识,从本篇开始,将会讲述并行 ...
- .NET 并行(多核)编程系列之五 Task执行和异常处理
原文:.NET 并行(多核)编程系列之五 Task执行和异常处理 .NET 并行(多核)编程系列之五 Task执行和异常处理 前言:本篇主要讲述等待task执行完成. 本篇的议题如下: 1. 等待Ta ...
- .NET 并行(多核)编程系列之六 Task基础部分完结篇
原文:.NET 并行(多核)编程系列之六 Task基础部分完结篇 .NET 并行(多核)编程系列之六 Task基础部分完结篇 前言:之前的文章介绍了了并行编程的一些基本的,也注重的讲述了Task的一些 ...
- 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三
手把手叫你玩转网络编程系列之三 完毕port(Completion Port)具体解释 ...
- paip.提升性能---并行多核编程哈的数据结构list,set,map
paip.提升性能---并行多核编程哈的数据结构list,set,map vector/copyonwritearraylist 都是线程安全的. 或者经过包装的list ::: collection ...
- TCP/IP网络编程系列之三(初级)
TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...
- .Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果
尝试创建基于MVVM三层架构的异步任务: 场景:View层触发ViewModel层的动作请求,ViewModel层异步的从Model层查询数据,当数据返回或者请求超时时正确更新ViewModel层数据 ...
- (转载)完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三
转自:http://blog.csdn.net/piggyxp/article/details/6922277 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何 ...
- 【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监測EV3port数据
在前两篇文章中,我们对iOS与EV3混合机器人编程做了一个主要的设想.而且介绍了要完毕项目所需的软硬件准备和知识准备. 那么在今天这一篇文章中,我们将直接真正開始项目实践. ==第一个项目: EV3 ...
随机推荐
- JavaScript发展史,与JScript差别,引入方式,数据类型,命名规范,命名推荐,解决命名冲突
文件夹: 1.JavaScript发展史 2.JavaScript与JScript差别 3.JavaScript引入方式 4.JavaScript基本数据类型及布尔值 5.JavaScript命名规范 ...
- 单例模式 - 程序实现(Java)
我们知道单例模式,其实就是返回一个被调用类的实例. 在频繁的进行实例(Instance)创建过程,难免过多的进行new InstanceName():我们可以只通过调用一个方法解决. 在进行设计模式的 ...
- poj1679(最小生成树)
传送门:The Unique MST 题意:判断最小生成树是否唯一. 分析:先求出原图的最小生成树,然后枚举删掉最小生成树的边,重做kruskal,看新的值和原值是否一样,一样的话最小生成树不唯一. ...
- 二、第一个ExtJS程序:helloExtJS
开发前的准备 下载并解压ExtJS包后,能够得到下图的文件文件夹结构: 在实际开发过程中并不须要全部的文件和文件夹,所需的包括例如以下文件夹就可以: 若使用eclipse进行开发,仅仅需将上述文件拷贝 ...
- 解决方式:QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins
在用QSqlDatabase时遇到报错QSqlDatabase: an instance of QCoreApplication is required for loading driver plug ...
- 下拉刷新,上拉装载许多其他ListView
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVuaHVhaG91c2U=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- 用XAML做网页!!—开篇
原文:用XAML做网页!!-开篇 这几日一直没发表新文章,一来是因为事比较多,二来就是我在研究使用XAML挑战传统HTML来做网页,这很可能是在全球的首次尝试,至少我从未找到任何可供参考的相关资料. ...
- java-IO操作性能对照
在软件系统中.IO速度比内存速度慢,IO读写在非常多情况下会是系统的瓶颈. 在java标准IO操作中,InputStream和OutputStream提供基于流的IO操作.以字节为处理单位:Reade ...
- <EditText /> This text field does not specify an inputType or a hint
我是一个警告,xml代码是: <EditText android:id="@+id/str_ipaddress" android:layout_width="wra ...
- Linux下多线程查看工具(pstree、ps、pstack) (转)
1. pstree pstree以树结构显示进程$ pstree -p work | grep adsshd(22669)---bash(22670)---ad_preprocess(4551)-+- ...