[英]Cancellation token in Task constructor: why?
某些System.Threading.Tasks.Task
構造函數將CancellationToken
作為參數:
CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);
對此讓我感到困惑的是,沒有辦法從方法主體內部實際獲取傳入的令牌(例如,沒有像Task.CurrentTask.CancellationToken
那樣的Task.CurrentTask.CancellationToken
)。 令牌必須通過一些其他機制提供,例如狀態對象或在 lambda 中捕獲。
那么在構造函數中提供取消令牌有什么作用呢?
將CancellationToken
傳遞到Task
構造函數將其與任務相關聯。
這有兩個主要好處:
- 如果令牌在
Task
開始執行之前請求取消,則Task
將不會執行。 它不會轉換到Running
,而是立即轉換到Canceled
。 這避免了運行任務的成本,如果它無論如何都會在運行時被取消。- 如果任務的主體也在監視取消標記並拋出一個包含該標記的
OperationCanceledException
(這就是ThrowIfCancellationRequested
所做的),那么當任務看到OperationCanceledException
,它會檢查OperationCanceledException
的標記是否與任務的標記匹配。 如果是,則該異常被視為對協作取消的確認,並且Task
轉換到Canceled
狀態(而不是Faulted
狀態)。
構造函數在內部使用令牌進行取消處理。 如果您的代碼想要訪問令牌,您有責任將其傳遞給自己。 我強烈建議您閱讀CodePlex 上的Parallel Programming with 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();
取消並不像許多人想象的那樣簡單。 在 msdn 上的這篇博文中解釋了一些微妙之處:
例如:
在 Parallel Extensions 和其他系統中的某些情況下,有必要出於非用戶明確取消的原因喚醒被阻止的方法。 例如,如果一個線程由於集合為空而在
blockingCollection.Take()
上被阻塞,而另一個線程隨后調用了blockingCollection.CompleteAdding()
,那么第一個調用應該被喚醒並拋出一個InvalidOperationException
以表示不正確的用法。
這是一個代碼示例,演示了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!!!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.