某些System.Threading.Tasks.Task构造函数将CancellationToken作为参数:
CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);
让我困惑的是,在方法体内部没有办法实际获取传入的令牌(例如,没有Task.CurrentTask.CancellationToken)。令牌必须通过其他机制提供,例如状态对象或在lambda中捕获。
那么在构造函数中提供取消令牌的目的是什么呢?
将CancellationToken传递给Task构造函数,将其与任务关联起来。
引用来自MSDN的Stephen Toub的回答:
This has two primary benefits:
If the token has cancellation requested prior to the Task starting to execute, the Task won't execute. Rather than transitioning to
Running, it'll immediately transition to Canceled. This avoids the
costs of running the task if it would just be canceled while running
anyway.
If the body of the task is also monitoring the cancellation token and throws an OperationCanceledException containing that token
(which is what ThrowIfCancellationRequested does), then when the task
sees that OperationCanceledException, it checks whether the OperationCanceledException's token matches the Task's
token. If it does, that exception is viewed as an acknowledgement of
cooperative cancellation and the Task transitions to the Canceled
state (rather than the Faulted state).
将CancellationToken传递给Task构造函数,将其与任务关联起来。
引用来自MSDN的Stephen Toub的回答:
This has two primary benefits:
If the token has cancellation requested prior to the Task starting to execute, the Task won't execute. Rather than transitioning to
Running, it'll immediately transition to Canceled. This avoids the
costs of running the task if it would just be canceled while running
anyway.
If the body of the task is also monitoring the cancellation token and throws an OperationCanceledException containing that token
(which is what ThrowIfCancellationRequested does), then when the task
sees that OperationCanceledException, it checks whether the OperationCanceledException's token matches the Task's
token. If it does, that exception is viewed as an acknowledgement of
cooperative cancellation and the Task transitions to the Canceled
state (rather than the Faulted state).
下面是一个代码示例,演示了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!!!