某些System.Threading.Tasks.Task
构造函数将CancellationToken
作为参数:
CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);
让我感到困惑的是,方法体内的无法实际获取传入的令牌(例如,不像Task.CurrentTask.CancellationToken
)。必须通过其他一些机制提供令牌,例如状态对象或在lambda中捕获。
那么在构造函数中提供取消令牌的目的是什么呢?
答案 0 :(得分:246)
将此标记传递给Task构造函数会将其与此任务相关联。
引用Stephen Toub's answer from MSDN:
这有两个主要好处:
- 如果令牌在任务开始执行之前已请求取消,则任务将不会执行。而不是过渡到
Running
,它会立即转换为Canceled
。这避免了 如果在运行时只是取消任务,则运行任务的成本 反正。- 如果任务正文还监视取消令牌并抛出包含该令牌的
醇>OperationCanceledException
(这是ThrowIfCancellationRequested
所做的),然后当任务 看到OperationCanceledException
,它检查OperationCanceledException
的令牌是否与任务的匹配 令牌。如果是,则该异常被视为对此的确认 合作取消和任务过渡到已取消 国家(而不是Faulted州)。
答案 1 :(得分:28)
构造函数在内部使用令牌进行取消处理。如果您的代码想要访问令牌,则您有责任将其传递给自己。我强烈建议您阅读Parallel Programming with Microsoft .NET book at CodePlex。
本书中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();
答案 2 :(得分:7)
取消并不像许多人想象的那么简单。 msdn:
上的博客文章解释了一些细微之处例如:
在Parallel Extensions和其他系统中的某些情况下,它 因为未到期的原因而唤醒被阻止的方法是必要的 明确取消用户。例如,如果一个线程是 由于集合为空,阻塞在blockingCollection.Take()上 然后另一个线程调用 blockingCollection.CompleteAdding(),然后第一次调用应该唤醒 up并抛出InvalidOperationException来表示不正确的 的使用。
http://blogs.msdn.com/b/pfxteam/archive/2009/06/22/9791840.aspx
答案 3 :(得分:5)
下面是一个代码示例,演示了accepted answer的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;
}
}
}
}
输出:
*********************************************************************
* 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!!!