Gewusst wie: Abbrechen einer Aufgabe und ihrer untergeordneten Elemente
In diesen Beispielen wird gezeigt, wie Sie die folgenden Aufgaben ausführen:
Erstellen und Starten einer abbrechbare Aufgabe
Übergeben eines Abbruchtokens an den Benutzerdelegaten und optional an die Aufgabeninstanz
Erkennen der Abbruchanforderung im Benutzerdelegaten und Reagieren auf diese
Hinzufügen eines optionalen Hinweises, dass die Aufgabe abgebrochen wurde, zum aufrufenden Thread
Der aufrufende Thread erzwingt nicht die Beendigung der Aufgabe, er kennzeichnet nur, dass der Abbruch angefordert wurde. Wenn die Aufgabe bereits ausgeführt wird, muss der Benutzerdelegat die Anforderung erkennen und entsprechend reagieren. Wenn der Abbruch vor Auführung der Aufgabe angefordert wird, wird der Benutzerdelegat nicht ausgeführt; und das Aufgabenobjekt geht in den abgebrochenen Zustand über.
Beispiel
In diesem Beispiel wird gezeigt, wie Sie Task und die zugehörigen untergeordneten Elemente als Reaktion auf eine Abbruchanforderung beenden. Darüber hinaus wird gezeigt, dass beim Beenden eines Benutzerdelegaten durch eine OperationCanceledException der aufrufende Thread optional die Wait-Methode oder WaitAll-Methode verwenden kann, um auf das Ende der Aufgaben zu warten. In diesem Fall muss der Delegat die Ausnahmen im aufrufenden Thread mithilfe eines try/catch-Blocks behandeln.
' How to: Cancel a Task and Its Children
Imports System.Threading
Imports System.Threading.Tasks
Module CancelATask
Sub Main()
Console.WriteLine("Press any key to start. Press 'c' to cancel.")
Console.ReadKey()
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 tasks(10) As Task
' 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.
tasks(0) = Task.Factory.StartNew(Sub() DoSomeWork(1, token), token)
' 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.
tasks(1) = Task.Factory.StartNew(Sub()
' Create some cancelable child tasks.
For i As Integer = 2 To 10
' For each child task, pass the same token
' to each user delegate and to StartNew.
tasks(i) = Task.Factory.StartNew(Sub(iteration) DoSomeWork(iteration, token), i, token)
' Passing 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)
Next
End Sub _
, token)
' Give the tasks a second to start.
Thread.Sleep(1000)
' Request cancellation from the UI thread.
If Console.ReadKey().KeyChar = "c"c Then
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 OCE 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)
Catch e As AggregateException
' For demonstration purposes, show the OCE message.
For Each v In e.InnerExceptions
Console.WriteLine("msg: " + v.Message)
Next
End Try
' Prove that the tasks are now all in a canceled state.
For i As Integer = 0 To tasks.Length
Console.WriteLine("task(0) status is now 1", i, tasks(i).Status)
Next
' Keep the console window open while the
' task completes its output.
Console.ReadLine()
End Sub
Sub DoSomeWork(ByVal taskNum As Integer, ByVal ct As CancellationToken)
' Was cancellation already requested?
If ct.IsCancellationRequested = True Then
Console.WriteLine("We were cancelled before we got started.")
Console.WriteLine("Press Enter to quit.")
ct.ThrowIfCancellationRequested()
End If
Dim maxIterations As Integer = 1000
' NOTE!!! An "OperationCanceledException 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 3000
sw.SpinOnce()
Next
Console.WriteLine("...0 ", taskNum)
If ct.IsCancellationRequested Then
Console.WriteLine("bye from 0.", taskNum)
Console.WriteLine("\nPress Enter to quit.")
ct.ThrowIfCancellationRequested()
End If
Next
End Sub
End Module
namespace CancellationWithOCE
{
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press any key to start. Press 'c' to cancel.");
Console.ReadKey();
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[] tasks = new Task[10];
// 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.
tasks[0] = Task.Factory.StartNew(() => DoSomeWork(1, token), token);
// 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.
tasks[1] = Task.Factory.StartNew(() =>
{
// Create some cancelable child tasks.
for (int i = 2; i < 10; i++)
{
// For each child task, pass the same token
// to each user delegate and to StartNew.
tasks[i] = Task.Factory.StartNew(iteration =>
DoSomeWork((int)iteration, token), i, token);
}
// Passing 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);
// Give the tasks a second to start.
Thread.Sleep(1000);
// Request cancellation from the UI thread.
if (Console.ReadKey().KeyChar == '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 OCE is thrown if the token that was passed to the
// StartNew method is the same token that requested the cancellation.
#region Optional_WaitOnTasksToComplete
try
{
Task.WaitAll(tasks);
}
catch (AggregateException e)
{
// For demonstration purposes, show the OCE message.
foreach (var v in e.InnerExceptions)
Console.WriteLine("msg: " + v.Message);
}
// Prove that the tasks are now all in a canceled state.
for (int i = 0; i < tasks.Length; i++)
Console.WriteLine("task[{0}] status is now {1}", i, tasks[i].Status);
#endregion
}
// Keep the console window open while the
// task completes its output.
Console.ReadLine();
}
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Was cancellation already requested?
if (ct.IsCancellationRequested)
{
Console.WriteLine("We were cancelled before we got started.");
Console.WriteLine("Press Enter to quit.");
ct.ThrowIfCancellationRequested();
}
int maxIterations = 1000;
// NOTE!!! A benign "OperationCanceledException was unhandled
// by user code" error might be raised here. Press F5 to continue. Or,
// to avoid the error, uncheck the "Enable Just My Code"
// option under Tools > Options > Debugging.
for (int i = 0; i < maxIterations; i++)
{
// Do a bit of work. Not too much.
var sw = new SpinWait();
for (int j = 0; j < 3000; j++) sw.SpinOnce();
Console.WriteLine("...{0} ", taskNum);
if (ct.IsCancellationRequested)
{
Console.WriteLine("bye from {0}.", taskNum);
Console.WriteLine("\nPress Enter to quit.");
ct.ThrowIfCancellationRequested();
}
}
}
}
}
Die System.Threading.Tasks.Task-Klasse ist vollständig in das Abbruchmodell integriert, das auf dem System.Threading.CancellationTokenSource-Typ und dem System.Threading.CancellationToken-Typ basiert. Weitere Informationen finden Sie unter Abbruch und Aufgabenabbruch.
Siehe auch
Referenz
System.Threading.CancellationTokenSource
System.Threading.CancellationToken
System.Threading.Tasks.Task<TResult>
Konzepte
Lambda-Ausdrücke in PLINQ und TPL