방법: 작업 및 해당 자식 취소
이 예제는 다음 작업을 수행하는 방법을 보여줍니다.
취소 가능한 작업을 만들고 시작합니다.
사용자 대리자에 그리고 선택적으로 작업 인스턴스에 취소 토큰을 전달합니다.
사용자 대리자의 취소 요청을 확인하고 이에 응답합니다.
선택적으로 작업이 취소된 호출 스레드를 확인합니다.
호출 스레드가 작업을 강제로 종료하지 않으며, 취소가 요청되었다는 신호만 보냅니다. 작업이 이미 실행 중인 경우 사용자 대리자가 요청을 알리고 적절하게 응답해야 합니다. 작업 실행 전에 취소를 요청하면 사용자 대리자가 실행되지 않고 작업 개체가 Canceled 상태로 전환됩니다.
예시
이 예제는 취소 요청에 대한 응답으로 Task 및 해당 자식을 종료하는 방법을 보여줍니다. 또한 TaskCanceledException을 throw하여 사용자 대리자가 종료될 때 호출 스레드에서 필요에 따라 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 유형을 기반으로 하는 취소 모델과 완전히 통합되었습니다. 자세한 내용은 관리되는 스레드의 취소 및 작업 취소를 참조하세요.
참고 항목
.NET
피드백
https://aka.ms/ContentUserFeedback
출시 예정: 2024년 내내 콘텐츠에 대한 피드백 메커니즘으로 GitHub 문제를 단계적으로 폐지하고 이를 새로운 피드백 시스템으로 바꿀 예정입니다. 자세한 내용은 다음을 참조하세요.다음에 대한 사용자 의견 제출 및 보기