如何:取消工作及其子系
這些範例示範如何執行下列工作:
建立和啟動可取消的工作。
將取消權杖傳送至您的使用者委派或工作執行個體 (選擇性)。
注意並回應使用者委派中的取消要求。
注意已取消工作的呼叫端執行緒 (選擇性)。
呼叫端執行緒不會強制結束工作;它只會通知已要求取消。 如果工作已在執行,則是由使用者委派來通知要求並適當回應。 如果在工作執行前要求取消,則永遠不會執行使用者委派,且工作物件會轉換成 Canceled 狀態。
範例
此範例示範如何終止 Task 及其子系,以回應取消要求。 這個範例也會說明在使用者委派藉由擲回 TaskCanceledException 結束時,呼叫端執行緒可以選擇性地使用 Wait 方法或 WaitAll 方法等候工作完成。 在這種情況下,您必須使用 try/catch
區塊來處理呼叫端執行緒上的例外狀況。
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static async Task Main()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
// Store references to the tasks so that we can wait on them and
// observe their status after cancellation.
Task t;
var tasks = new ConcurrentBag<Task>();
Console.WriteLine("Press any key to begin tasks...");
Console.ReadKey(true);
Console.WriteLine("To terminate the example, press 'c' to cancel and exit...");
Console.WriteLine();
// Request cancellation of a single task when the token source is canceled.
// Pass the token to the user delegate, and also to the task so it can
// handle the exception correctly.
t = Task.Run(() => DoSomeWork(token), token);
Console.WriteLine("Task {0} executing", t.Id);
tasks.Add(t);
// Request cancellation of a task and its children. Note the token is passed
// to (1) the user delegate and (2) as the second argument to Task.Run, so
// that the task instance can correctly handle the OperationCanceledException.
t = Task.Run(() =>
{
// Create some cancelable child tasks.
Task tc;
for (int i = 3; i <= 10; i++)
{
// For each child task, pass the same token
// to each user delegate and to Task.Run.
tc = Task.Run(() => DoSomeWork(token), token);
Console.WriteLine("Task {0} executing", tc.Id);
tasks.Add(tc);
// Pass the same token again to do work on the parent task.
// All will be signaled by the call to tokenSource.Cancel below.
DoSomeWork(token);
}
}, token);
Console.WriteLine("Task {0} executing", t.Id);
tasks.Add(t);
// Request cancellation from the UI thread.
char ch = Console.ReadKey().KeyChar;
if (ch == 'c' || ch == 'C')
{
tokenSource.Cancel();
Console.WriteLine("\nTask cancellation requested.");
// Optional: Observe the change in the Status property on the task.
// It is not necessary to wait on tasks that have canceled. However,
// if you do wait, you must enclose the call in a try-catch block to
// catch the OperationCanceledExceptions that are thrown. If you do
// not wait, no exception is thrown if the token that was passed to the
// Task.Run method is the same token that requested the cancellation.
}
try
{
await Task.WhenAll(tasks.ToArray());
}
catch (OperationCanceledException)
{
Console.WriteLine($"\n{nameof(OperationCanceledException)} thrown\n");
}
finally
{
tokenSource.Dispose();
}
// Display status of all tasks.
foreach (var task in tasks)
Console.WriteLine("Task {0} status is now {1}", task.Id, task.Status);
}
static void DoSomeWork(CancellationToken ct)
{
// Was cancellation already requested?
if (ct.IsCancellationRequested)
{
Console.WriteLine("Task {0} was cancelled before it got started.",
Task.CurrentId);
ct.ThrowIfCancellationRequested();
}
int maxIterations = 100;
// NOTE!!! A "TaskCanceledException was unhandled
// by user code" error will be raised here if "Just My Code"
// is enabled on your computer. On Express editions JMC is
// enabled and cannot be disabled. The exception is benign.
// Just press F5 to continue executing your code.
for (int i = 0; i <= maxIterations; i++)
{
// Do a bit of work. Not too much.
var sw = new SpinWait();
for (int j = 0; j <= 100; j++)
sw.SpinOnce();
if (ct.IsCancellationRequested)
{
Console.WriteLine("Task {0} cancelled", Task.CurrentId);
ct.ThrowIfCancellationRequested();
}
}
}
}
// The example displays output like the following:
// Press any key to begin tasks...
// To terminate the example, press 'c' to cancel and exit...
//
// Task 1 executing
// Task 2 executing
// Task 3 executing
// Task 4 executing
// Task 5 executing
// Task 6 executing
// Task 7 executing
// Task 8 executing
// c
// Task cancellation requested.
// Task 2 cancelled
// Task 7 cancelled
//
// OperationCanceledException thrown
//
// Task 2 status is now Canceled
// Task 1 status is now RanToCompletion
// Task 8 status is now Canceled
// Task 7 status is now Canceled
// Task 6 status is now RanToCompletion
// Task 5 status is now RanToCompletion
// Task 4 status is now RanToCompletion
// Task 3 status is now RanToCompletion
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Sub Main()
Dim tokenSource As New CancellationTokenSource()
Dim token As CancellationToken = tokenSource.Token
' Store references to the tasks so that we can wait on them and
' observe their status after cancellation.
Dim t As Task
Dim tasks As New ConcurrentBag(Of Task)()
Console.WriteLine("Press any key to begin tasks...")
Console.ReadKey(True)
Console.WriteLine("To terminate the example, press 'c' to cancel and exit...")
Console.WriteLine()
' Request cancellation of a single task when the token source is canceled.
' Pass the token to the user delegate, and also to the task so it can
' handle the exception correctly.
t = Task.Factory.StartNew(Sub() DoSomeWork(token), token)
Console.WriteLine("Task {0} executing", t.Id)
tasks.Add(t)
' Request cancellation of a task and its children. Note the token is passed
' to (1) the user delegate and (2) as the second argument to StartNew, so
' that the task instance can correctly handle the OperationCanceledException.
t = Task.Factory.StartNew(Sub()
' Create some cancelable child tasks.
Dim tc As Task
For i As Integer = 3 To 10
' For each child task, pass the same token
' to each user delegate and to StartNew.
tc = Task.Factory.StartNew(Sub(iteration) DoSomeWork(token), i, token)
Console.WriteLine("Task {0} executing", tc.Id)
tasks.Add(tc)
' Pass the same token again to do work on the parent task.
' All will be signaled by the call to tokenSource.Cancel below.
DoSomeWork(token)
Next
End Sub,
token)
Console.WriteLine("Task {0} executing", t.Id)
tasks.Add(t)
' Request cancellation from the UI thread.
Dim ch As Char = Console.ReadKey().KeyChar
If ch = "c"c Or ch = "C"c Then
tokenSource.Cancel()
Console.WriteLine(vbCrLf + "Task cancellation requested.")
' Optional: Observe the change in the Status property on the task.
' It is not necessary to wait on tasks that have canceled. However,
' if you do wait, you must enclose the call in a try-catch block to
' catch the OperationCanceledExceptions that are thrown. If you do
' not wait, no exception is thrown if the token that was passed to the
' StartNew method is the same token that requested the cancellation.
End If
Try
Task.WaitAll(tasks.ToArray())
Catch e As AggregateException
Console.WriteLine()
Console.WriteLine("AggregateException thrown with the following inner exceptions:")
' Display information about each exception.
For Each v In e.InnerExceptions
If TypeOf v Is OperationCanceledException Then
Console.WriteLine(" The operation was canceled.")
Else
Console.WriteLine(" Exception: {0}", v.GetType().Name)
End If
Next
Console.WriteLine()
Finally
tokenSource.Dispose()
End Try
' Display status of all tasks.
For Each t In tasks
Console.WriteLine("Task {0} status is now {1}", t.Id, t.Status)
Next
End Sub
Sub DoSomeWork(ByVal ct As CancellationToken)
' Was cancellation already requested?
If ct.IsCancellationRequested = True Then
Console.WriteLine("Task {0} was cancelled before it got started.",
Task.CurrentId)
ct.ThrowIfCancellationRequested()
End If
Dim maxIterations As Integer = 100
' NOTE!!! A "TaskCanceledException was unhandled
' by user code" error will be raised here if "Just My Code"
' is enabled on your computer. On Express editions JMC is
' enabled and cannot be disabled. The exception is benign.
' Just press F5 to continue executing your code.
For i As Integer = 0 To maxIterations
' Do a bit of work. Not too much.
Dim sw As New SpinWait()
For j As Integer = 0 To 100
sw.SpinOnce()
Next
If ct.IsCancellationRequested Then
Console.WriteLine("Task {0} cancelled", Task.CurrentId)
ct.ThrowIfCancellationRequested()
End If
Next
End Sub
End Module
' The example displays output like the following:
' Press any key to begin tasks...
' To terminate the example, press 'c' to cancel and exit...
'
' Task 1 executing
' Task 2 executing
' Task 3 executing
' Task 4 executing
' Task 5 executing
' Task 6 executing
' Task 7 executing
' Task 8 executing
' c
' Task cancellation requested.
' Task 2 cancelled
' Task 7 cancelled
'
' AggregateException thrown with the following inner exceptions:
' TaskCanceledException: Task 2
' TaskCanceledException: Task 8
' TaskCanceledException: Task 7
'
' Task 2 status is now Canceled
' Task 1 status is now RanToCompletion
' Task 8 status is now Canceled
' Task 7 status is now Canceled
' Task 6 status is now RanToCompletion
' Task 5 status is now RanToCompletion
' Task 4 status is now RanToCompletion
' Task 3 status is now RanToCompletion
System.Threading.Tasks.Task 類別已和基礎是 System.Threading.CancellationTokenSource 及 System.Threading.CancellationToken 型別的取消模型完全整合。 如需詳細資訊,請參閱受控執行緒中的取消作業和工作取消。
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應