System.Threading.Tasks.Task 和 System.Threading.Tasks.Task
简单地从委托中返回。 在许多情况下,这样已足够;但是,采用这种方式取消的任务实例会转换为 TaskStatus.RanToCompletion 状态,而不是 TaskStatus.Canceled 状态。
引发 OperationCanceledException ,并将其传递到在其上请求了取消的标记。 完成此操作的首选方式是使用 ThrowIfCancellationRequested 方法。 采用这种方式取消的任务会转换为 Canceled 状态,调用代码可使用该状态来验证任务是否响应了其取消请求。
下面的示例演示引发异常的任务取消的基本模式。 请注意,标记将传递到用户委托和任务实例本身
- using System;
- using System.Threading;
- using System.Threading.Tasks;
-
- class Program
- {
- static async Task Main()
- {
- var tokenSource2 = new CancellationTokenSource();
- CancellationToken ct = tokenSource2.Token;
-
- var task = Task.Run(() =>
- {
- // Were we already canceled?
- ct.ThrowIfCancellationRequested();
-
- bool moreToDo = true;
- while (moreToDo)
- {
- // Poll on this property if you have to do
- // other cleanup before throwing.
- if (ct.IsCancellationRequested)
- {
- // Clean up here, then...
- ct.ThrowIfCancellationRequested();
- }
- }
- }, tokenSource2.Token); // Pass same token to Task.Run.
-
- tokenSource2.Cancel();
-
- // Just continue on this thread, or await with try-catch:
- try
- {
- await task;
- }
- catch (OperationCanceledException e)
- {
- Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
- }
- finally
- {
- tokenSource2.Dispose();
- }
-
- Console.ReadKey();
- }
- }
这些示例展示了如何执行下列任务:
创建并启动可取消任务。
将取消令牌传递给用户委托,并视需要传递给任务实例。
注意并响应用户委托中的取消请求。
(可选)注意已取消任务的调用线程。
调用线程不会强制结束任务,只会提示取消请求已发出。 如果任务已在运行,至于怎样才能注意请求并适当响应,取决于用户委托的选择。 如果取消请求在任务运行前发出,用户委托绝不会执行,任务对象的状态会转换为“已取消”。
此示例展示了如何终止 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
(); -
- 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(1, 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(i, 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(2, 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 TaskCanceledExceptions 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(int taskNum, CancellationToken ct)
- {
- // Was cancellation already requested?
- if (ct.IsCancellationRequested)
- {
- Console.WriteLine("Task {0} was cancelled before it got started.",
- taskNum);
- 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", taskNum);
- 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