Col*_*lin 214 c# .net-4.0 task-parallel-library cancellation-token
某些System.Threading.Tasks.Task
构造函数将a CancellationToken
作为参数:
CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);
Run Code Online (Sandbox Code Playgroud)
令我感到困惑的是,从方法体内部无法实际获取传入的令牌(例如,没有什么比这样Task.CurrentTask.CancellationToken
).必须通过某种其他机制提供令牌,例如状态对象或在lambda中捕获.
那么在构造函数中提供取消令牌的目的是什么呢?
Max*_*kin 246
将此标记传递给Task构造函数会将其与此任务相关联.
这有两个主要好处:
- 如果令牌在任务开始执行之前已请求取消,则任务将不会执行.它不会转换为Running,而是立即转换为Canceled.这样可以避免运行任务的成本,如果它在运行时只是被取消.
- 如果任务的主体也在监视取消令牌并抛出
Running
包含该令牌(这是ThrowIfCancellationRequested所做的那样),那么当任务看到该OCE时,它会检查OCE的令牌是否与Task的令牌匹配.如果是,则该异常被视为对协作取消的确认,并且任务转换到已取消状态(而不是故障状态).
use*_*116 28
构造函数在内部使用令牌进行取消处理.如果您的代码想要访问令牌,则您有责任将其传递给自己.我强烈建议您阅读CodePlex上的Microsoft .NET并行编程书籍.
本书中CTS的使用示例:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task myTask = Task.Factory.StartNew(() =>
{
for (...)
{
token.ThrowIfCancellationRequested();
// Body of for loop.
}
}, token);
// ... elsewhere ...
cts.Cancel();
Run Code Online (Sandbox Code Playgroud)
取消并不像许多人想象的那么简单.msdn上的这篇博文中解释了一些细微之处:
例如:
在并行扩展和其他系统中的某些情况下,必须唤醒被阻止的方法,原因不是由于用户明确取消.例如,如果由于集合为空而另一个线程随后调用了blockingCollection.CompleteAdding()而在blockingCollection.Take()上阻塞了一个线程,则第一个调用应该被唤醒并抛出InvalidOperationException来表示不正确的用法.
http://blogs.msdn.com/b/pfxteam/archive/2009/06/22/9791840.aspx
这是一个代码示例,演示了Max Galkin接受的答案中的两点:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("*********************************************************************");
Console.WriteLine("* Start canceled task, don't pass token to constructor");
Console.WriteLine("*********************************************************************");
StartCanceledTaskTest(false);
Console.WriteLine();
Console.WriteLine("*********************************************************************");
Console.WriteLine("* Start canceled task, pass token to constructor");
Console.WriteLine("*********************************************************************");
StartCanceledTaskTest(true);
Console.WriteLine();
Console.WriteLine("*********************************************************************");
Console.WriteLine("* Throw if cancellation requested, don't pass token to constructor");
Console.WriteLine("*********************************************************************");
ThrowIfCancellationRequestedTest(false);
Console.WriteLine();
Console.WriteLine("*********************************************************************");
Console.WriteLine("* Throw if cancellation requested, pass token to constructor");
Console.WriteLine("*********************************************************************");
ThrowIfCancellationRequestedTest(true);
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Test Completed!!!");
Console.ReadKey();
}
static void StartCanceledTaskTest(bool passTokenToConstructor)
{
Console.WriteLine("Creating task");
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task task = null;
if (passTokenToConstructor)
{
task = new Task(() => TaskWork(tokenSource.Token, false), tokenSource.Token);
}
else
{
task = new Task(() => TaskWork(tokenSource.Token, false));
}
Console.WriteLine("Canceling task");
tokenSource.Cancel();
try
{
Console.WriteLine("Starting task");
task.Start();
task.Wait();
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("InnerException: {0}", ex.InnerException.Message);
}
}
Console.WriteLine("Task.Status: {0}", task.Status);
}
static void ThrowIfCancellationRequestedTest(bool passTokenToConstructor)
{
Console.WriteLine("Creating task");
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task task = null;
if (passTokenToConstructor)
{
task = new Task(() => TaskWork(tokenSource.Token, true), tokenSource.Token);
}
else
{
task = new Task(() => TaskWork(tokenSource.Token, true));
}
try
{
Console.WriteLine("Starting task");
task.Start();
Thread.Sleep(100);
Console.WriteLine("Canceling task");
tokenSource.Cancel();
task.Wait();
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("InnerException: {0}", ex.InnerException.Message);
}
}
Console.WriteLine("Task.Status: {0}", task.Status);
}
static void TaskWork(CancellationToken token, bool throwException)
{
int loopCount = 0;
while (true)
{
loopCount++;
Console.WriteLine("Task: loop count {0}", loopCount);
token.WaitHandle.WaitOne(50);
if (token.IsCancellationRequested)
{
Console.WriteLine("Task: cancellation requested");
if (throwException)
{
token.ThrowIfCancellationRequested();
}
break;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
*********************************************************************
* Start canceled task, don't pass token to constructor
*********************************************************************
Creating task
Canceling task
Starting task
Task: loop count 1
Task: cancellation requested
Task.Status: RanToCompletion
*********************************************************************
* Start canceled task, pass token to constructor
*********************************************************************
Creating task
Canceling task
Starting task
Exception: Start may not be called on a task that has completed.
Task.Status: Canceled
*********************************************************************
* Throw if cancellation requested, don't pass token to constructor
*********************************************************************
Creating task
Starting task
Task: loop count 1
Task: loop count 2
Canceling task
Task: cancellation requested
Exception: One or more errors occurred.
InnerException: The operation was canceled.
Task.Status: Faulted
*********************************************************************
* Throw if cancellation requested, pass token to constructor
*********************************************************************
Creating task
Starting task
Task: loop count 1
Task: loop count 2
Canceling task
Task: cancellation requested
Exception: One or more errors occurred.
InnerException: A task was canceled.
Task.Status: Canceled
Test Completed!!!
Run Code Online (Sandbox Code Playgroud)