Abbruch in verwalteten ThreadsCancellation in Managed Threads

Seit .NET Framework 4.NET Framework 4 verwendet das .NET Framework ein einheitliches Modell für den kooperativen Abbruch von asynchronen oder lang andauernden synchronen Vorgängen.Starting with the .NET Framework 4.NET Framework 4, the .NET Framework uses a unified model for cooperative cancellation of asynchronous or long-running synchronous operations. Dieses Modell basiert auf einem einfachen Objekt, dem sogenannten "Abbruchtoken".This model is based on a lightweight object called a cancellation token. Das Objekt, das einen oder mehrere abbrechbare Vorgänge aufruft, z. B. durch Erstellen neuer Threads oder Aufgaben, übergibt das Token an jeden Vorgang.The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. Einzelne Vorgänge können wiederum Kopien des Tokens an andere Vorgänge übergeben.Individual operations can in turn pass copies of the token to other operations. Zu einem späteren Zeitpunkt kann das Objekt, das das Token erstellt hat, damit anfordern, dass die Vorgänge ihre aktuelle Aktivität einstellen.At some later time, the object that created the token can use it to request that the operations stop what they are doing. Nur das anfordernde Objekt kann die Abbruchanforderung ausgeben, und jeder Listener ist dafür verantwortlich, die Anforderung zu bemerken und angemessen und rechtzeitig darauf zu reagieren.Only the requesting object can issue the cancellation request, and each listener is responsible for noticing the request and responding to it in an appropriate and timely manner.

Das allgemeine Muster für die Implementierung des kooperativen Abbruchmodells lautet:The general pattern for implementing the cooperative cancellation model is:

  • Instanziieren Sie ein CancellationTokenSource-Objekt, das die Abbruchbenachrichtigung verwaltet und an die einzelnen Abbruchtoken sendet.Instantiate a CancellationTokenSource object, which manages and sends cancellation notification to the individual cancellation tokens.

  • Übergeben Sie das zurückgegebene Token über die CancellationTokenSource.Token-Eigenschaft an jeden Task oder Thread, der zum Lauschen verwendet wird, um den Abbruch zu bemerken.Pass the token returned by the CancellationTokenSource.Token property to each task or thread that listens for cancellation.

  • Stellen Sie einen Mechanismus für jeden Task oder Thread bereit, um auf den Abbruch zu reagieren.Provide a mechanism for each task or thread to respond to cancellation.

  • Rufen Sie die CancellationTokenSource.Cancel-Methode auf, um eine Benachrichtigung über den Abbruch bereitzustellen.Call the CancellationTokenSource.Cancel method to provide notification of cancellation.

Wichtig

Die CancellationTokenSource-Klasse implementiert die IDisposable-Schnittstelle.The CancellationTokenSource class implements the IDisposable interface. Sie sollten darauf achten, die CancellationTokenSource.Dispose-Methode aufzurufen, wenn Sie die Verwendung der Abbruchtokenquelle abgeschlossen haben, um alle darin enthaltenen, nicht verwalteten Ressourcen freizugeben.You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.

Die folgende Abbildung zeigt die Beziehung zwischen einer Tokenquelle und allen Kopien des Tokens.The following illustration shows the relationship between a token source and all the copies of its token.

CancellationTokenSource und AbbruchtokensCancellationTokenSource and cancellation tokens

Das neue Abbruchmodell vereinfacht die Erstellung von abbruchfähigen Anwendungen und Bibliotheken, und es unterstützt die folgenden Funktionen:The new cancellation model makes it easier to create cancellation-aware applications and libraries, and it supports the following features:

  • Der Abbruch ist kooperativ und wird für den Listener nicht erzwungen.Cancellation is cooperative and is not forced on the listener. Der Listener bestimmt, wie die ordnungsgemäße Beendigung als Reaktion auf eine Abbruchanforderung durchgeführt wird.The listener determines how to gracefully terminate in response to a cancellation request.

  • Die Anforderung unterscheidet sich vom Lauschvorgang.Requesting is distinct from listening. Ein Objekt, das einen abbrechbaren Vorgang aufruft, kann steuern, wann (falls überhaupt) der Abbruch angefordert wird.An object that invokes a cancelable operation can control when (if ever) cancellation is requested.

  • Das anfordernde Objekt sendet die Abbruchanforderung mithilfe eines einzigen Methodenaufrufs an alle Kopien des Tokens.The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • Ein Listener kann mehrere Token gleichzeitig belauschen, indem diese zu einem verknüpften Token verbunden werden.A listener can listen to multiple tokens simultaneously by joining them into one linked token.

  • Benutzercode kann Abbruchanforderungen aus Bibliothekscode erkennen und auf diese reagieren, während Bibliothekscode Abbruchanforderungen aus Benutzercode erkennen und auf diese reagieren kann.User code can notice and respond to cancellation requests from library code, and library code can notice and respond to cancellation requests from user code.

  • Listener können durch Abruf, Rückrufregistrierung oder Warten auf Wait-Handles über Abbruchanforderungen benachrichtigt werden.Listeners can be notified of cancellation requests by polling, callback registration, or waiting on wait handles.

AbbruchtypenCancellation Types

Das Abbruchframework ist als Gruppe von verwandten Typen implementiert, die in der folgenden Tabelle aufgeführt sind.The cancellation framework is implemented as a set of related types, which are listed in the following table.

TypnameType name descriptionDescription
CancellationTokenSource Ein Objekt, das ein Abbruchtoken erstellt und auch die Abbruchanforderung für alle Kopien dieses Token ausgibt.Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
CancellationToken Ein einfacher Werttyp, der in der Regel als Methodenparameter an mindestens einen Listener übergeben wird.Lightweight value type passed to one or more listeners, typically as a method parameter. Listener überwachen den Wert der IsCancellationRequested-Eigenschaft des Token durch Abruf, Rückruf oder Wait-Handle.Listeners monitor the value of the IsCancellationRequested property of the token by polling, callback, or wait handle.
OperationCanceledException Überladungen des Konstruktors dieser Ausnahme akzeptieren ein CancellationToken als Parameter.Overloads of this exception's constructor accept a CancellationToken as a parameter. Listener können diese Ausnahme optional auslösen, um die Quelle des Abbruchs zu überprüfen und andere darüber zu benachrichtigen, dass auf eine Abbruchanforderung reagiert wurde.Listeners can optionally throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

Das neue Abbruchmodell ist in .NET Framework.NET Framework in mehreren Formen integriert.The new cancellation model is integrated into the .NET Framework.NET Framework in several types. Die wichtigsten sind System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> und System.Linq.ParallelEnumerable.The most important ones are System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> and System.Linq.ParallelEnumerable. Sie sollten dieses neue Abbruchmodell für sämtlichen neuen Bibliotheks- und Anwendungscode verwenden.We recommend that you use this new cancellation model for all new library and application code.

CodebeispielCode Example

Im folgenden Beispiel erstellt das anfordernde Objekt ein CancellationTokenSource-Objekt und übergibt dann seine Token-Eigenschaft an den abbrechbaren Vorgang.In the following example, the requesting object creates a CancellationTokenSource object, and then passes its Token property to the cancelable operation. Der Vorgang, der die Anforderung empfängt, überwacht den Wert von der IsCancellationRequested-Eigenschaft des Token durch Abruf.The operation that receives the request monitors the value of the IsCancellationRequested property of the token by polling. Wenn der Wert zu true wechselt, kann der Listener auf geeignete Weise beendet werden.When the value becomes true, the listener can terminate in whatever manner is appropriate. In diesem Beispiel wird die Methode einfach beendet und das ist auch häufig alles, was erforderlich ist.In this example, the method just exits, which is all that is required in many cases.

Hinweis

Im Beispiel wird die QueueUserWorkItem-Methode verwendet, um zu veranschaulichen, dass das neue Abbruchframework mit Legacy-APIs kompatibel ist.The example uses the QueueUserWorkItem method to demonstrate that the new cancellation framework is compatible with legacy APIs. Ein Beispiel, das den neuen bevorzugten System.Threading.Tasks.Task-Typ verwendet, finden Sie unter Gewusst wie: Abbrechen einer Aufgabe.For an example that uses the new, preferred System.Threading.Tasks.Task type, see How to: Cancel a Task and Its Children.

using System;
using System.Threading;

public class Example
{
   public static void Main()
   {
      // Create the token source.
      CancellationTokenSource cts = new CancellationTokenSource();

      // Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
      Thread.Sleep(2500);

      // Request cancellation.
      cts.Cancel();
      Console.WriteLine("Cancellation set in token source...");
      Thread.Sleep(2500);
      // Cancellation should have happened, so call Dispose.
      cts.Dispose();
   }

   // Thread 2: The listener
   static void DoSomeWork(object obj)
   {
      CancellationToken token = (CancellationToken)obj;

      for (int i = 0; i < 100000; i++) {
         if (token.IsCancellationRequested)
         {
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1);
            // Perform cleanup if necessary.
            //...
            // Terminate the operation.
            break;
         }
         // Simulate some work.
         Thread.SpinWait(500000);
      }
   }
}
// The example displays output like the following:
//       Cancellation set in token source...
//       In iteration 1430, cancellation has been requested...
Imports System.Threading

Module Example
   Public Sub Main()
      ' Create the token source.
      Dim cts As New CancellationTokenSource()

      ' Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoSomeWork), cts.Token)
      Thread.Sleep(2500)
        
      ' Request cancellation by setting a flag on the token.
      cts.Cancel()
      Console.WriteLine("Cancellation set in token source...")
      Thread.Sleep(2500)
      ' Cancellation should have happened, so call Dispose.
      cts.Dispose()
   End Sub

   ' Thread 2: The listener
   Sub DoSomeWork(ByVal obj As Object)
      Dim token As CancellationToken = CType(obj, CancellationToken)
      
      For i As Integer = 0 To 1000000
         If token.IsCancellationRequested Then
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1)
            ' Perform cleanup if necessary.
            '...
            ' Terminate the operation.
            Exit For
         End If
      
         ' Simulate some work.
         Thread.SpinWait(500000)
      Next
   End Sub
End Module
' The example displays output like the following:
'       Cancellation set in token source...
'       In iteration 1430, cancellation has been requested...

Vorgangsabbruch im Vergleich zum ObjektabbruchOperation Cancellation Versus Object Cancellation

Im neuen Abbruchframework bezieht sich Abbruch auf Vorgänge und nicht auf Objekte.In the new cancellation framework, cancellation refers to operations, not objects. Die Abbruchanforderung bedeutet, dass der Vorgang so schnell wie möglich beendet werden soll, nachdem alle erforderlichen Bereinigungen ausgeführt wurden.The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. Ein Abbruchtoken sollte auf einen "abbrechbaren Vorgang" verweisen, aber dieser Vorgang kann in Ihrem Programm implementiert werden.One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. Nachdem die IsCancellationRequested-Eigenschaft des Tokens auf true festgelegt wurde, kann sie nicht wieder auf false zurückgesetzt werden.After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. Daher können Abbruchtoken nicht wiederverwendet werden, nachdem sie abgebrochen wurden.Therefore, cancellation tokens cannot be reused after they have been canceled.

Wenn Sie einen Objektabbruchmechanismus benötigen, können Sie ihn durch Aufrufen der CancellationToken.Register-Methode auf dem Vorgangsabbruchmechanismus basieren lassen, wie im folgenden Beispiel gezeigt.If you require an object cancellation mechanism, you can base it on the operation cancellation mechanism by calling the CancellationToken.Register method, as shown in the following example.

using System;
using System.Threading;

class CancelableObject
{
   public string id;
   
   public CancelableObject(string id)
   {
      this.id = id;
   }
   
   public void Cancel() 
   { 
      Console.WriteLine("Object {0} Cancel callback", id);
      // Perform object cancellation here.
   }
}

public class Example
{
   public static void Main()
   {
      CancellationTokenSource cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;

      // User defined Class with its own method for cancellation
      var obj1 = new CancelableObject("1");
      var obj2 = new CancelableObject("2");
      var obj3 = new CancelableObject("3");

      // Register the object's cancel method with the token's
      // cancellation request.
      token.Register(() => obj1.Cancel());
      token.Register(() => obj2.Cancel());
      token.Register(() => obj3.Cancel());

      // Request cancellation on the token.
      cts.Cancel();
      // Call Dispose when we're done with the CancellationTokenSource.
      cts.Dispose();
   }
}
// The example displays the following output:
//       Object 3 Cancel callback
//       Object 2 Cancel callback
//       Object 1 Cancel callback
Imports System.Threading

Class CancelableObject
   Public id As String
   
   Public Sub New(id As String)
      Me.id = id
   End Sub
   
   Public Sub Cancel() 
      Console.WriteLine("Object {0} Cancel callback", id)
      ' Perform object cancellation here.
   End Sub
End Class

Module Example
   Public Sub Main()
        Dim cts As New CancellationTokenSource()
        Dim token As CancellationToken = cts.Token

        ' User defined Class with its own method for cancellation
        Dim obj1 As New CancelableObject("1")
        Dim obj2 As New CancelableObject("2")
        Dim obj3 As New CancelableObject("3")

        ' Register the object's cancel method with the token's
        ' cancellation request.
        token.Register(Sub() obj1.Cancel())
        token.Register(Sub() obj2.Cancel())
        token.Register(Sub() obj3.Cancel())

        ' Request cancellation on the token.
        cts.Cancel()
        ' Call Dispose when we're done with the CancellationTokenSource.
        cts.Dispose()
   End Sub
End Module
' The example displays output like the following:
'       Object 3 Cancel callback
'       Object 2 Cancel callback
'       Object 1 Cancel callback

Wenn ein Objekt mehrere gleichzeitig abbrechbare Vorgänge unterstützt, übergeben Sie ein separates Token als Eingabe für die einzelnen abbrechbaren Vorgänge.If an object supports more than one concurrent cancelable operation, pass a separate token as input to each distinct cancelable operation. Auf diese Weise kann ein Vorgang ohne Auswirkung auf die anderen abgebrochen werden.That way, one operation can be cancelled without affecting the others.

Lauschen und Reagieren auf AbbruchanforderungenListening and Responding to Cancellation Requests

Im Benutzerdelegaten bestimmt der Implementierer eines abbrechbaren Vorgangs, wie der Vorgang als Reaktion auf eine Abbruchanforderung beendet wird.In the user delegate, the implementer of a cancelable operation determines how to terminate the operation in response to a cancellation request. In vielen Fällen kann der Benutzerdelegat einfach die erforderliche Bereinigung ausführen und dann sofort zurückkehren.In many cases, the user delegate can just perform any required cleanup and then return immediately.

In komplexeren Fällen ist es für den Benutzerdelegaten möglicherweise erforderlich, den Bibliothekscode darüber zu benachrichtigen, dass der Abbruch aufgetreten ist.However, in more complex cases, it might be necessary for the user delegate to notify library code that cancellation has occurred. In solchen Fällen besteht für den Delegaten die richtige Methode zum Beenden des Vorgangs darin, die ThrowIfCancellationRequested-Methode aufzurufen, die zum Auslösen einer OperationCanceledException führt.In such cases, the correct way to terminate the operation is for the delegate to call the ThrowIfCancellationRequested, method, which will cause an OperationCanceledException to be thrown. Bibliothekscode kann diese Ausnahme im Benutzerdelegatthread abfangen und das Token der Ausnahme untersuchen, um zu ermitteln, ob die Ausnahme auf einen kooperativen Abbruch oder eine andere Ausnahmesituation hinweist.Library code can catch this exception on the user delegate thread and examine the exception's token to determine whether the exception indicates cooperative cancellation or some other exceptional situation.

Die Task-Klasse behandelt OperationCanceledException auf diese Weise.The Task class handles OperationCanceledException in this way. Weitere Informationen finden Sie unter Aufgabenabbruch.For more information, see Task Cancellation.

Lauschen durch AbrufListening by Polling

Bei langandauernden Berechnungen mit Schleifen oder Rekursionen können Sie auf eine Abbruchanforderung lauschen, indem Sie den Wert der CancellationToken.IsCancellationRequested-Eigenschaft in regelmäßigen Abständen abfragen.For long-running computations that loop or recurse, you can listen for a cancellation request by periodically polling the value of the CancellationToken.IsCancellationRequested property. Wenn der Wert true lautet, sollte die Methode die Bereinigung vornehmen und so schnell wie möglich beendet werden.If its value is true, the method should clean up and terminate as quickly as possible. Die optimale Häufigkeit für das Abrufen hängt vom Typ der Anwendung ab.The optimal frequency of polling depends on the type of application. Es ist Aufgabe des Entwicklers, die beste Abrufhäufigkeit für ein Programm zu ermitteln.It is up to the developer to determine the best polling frequency for any given program. Der Abruf selbst hat keinen signifikanten Einfluss auf die Leistung.Polling itself does not significantly impact performance. Im folgenden Beispiel wird eine Möglichkeit für den Abruf veranschaulicht.The following example shows one possible way to poll.

static void NestedLoops(Rectangle rect, CancellationToken token)
{
   for (int x = 0; x < rect.columns && !token.IsCancellationRequested; x++) {
      for (int y = 0; y < rect.rows; y++) {
         // Simulating work.
         Thread.SpinWait(5000);
         Console.Write("{0},{1} ", x, y);
      }

      // Assume that we know that the inner loop is very fast.
      // Therefore, checking once per row is sufficient.
      if (token.IsCancellationRequested) {
         // Cleanup or undo here if necessary...
         Console.WriteLine("\r\nCancelling after row {0}.", x);
         Console.WriteLine("Press any key to exit.");
         // then...
         break;
         // ...or, if using Task:
         // token.ThrowIfCancellationRequested();
      }
   }
}
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
    For x As Integer = 0 To rect.columns
        For y As Integer = 0 To rect.rows
            ' Simulating work.
            Thread.SpinWait(5000)
            Console.Write("0' end block,1' end block ", x, y)
        Next

        ' Assume that we know that the inner loop is very fast.
        ' Therefore, checking once per row is sufficient.
        If token.IsCancellationRequested = True Then
            ' Cleanup or undo here if necessary...
            Console.WriteLine(vbCrLf + "Cancelling after row 0' end block.", x)
            Console.WriteLine("Press any key to exit.")
            ' then...
            Exit For
            ' ...or, if using Task:
            ' token.ThrowIfCancellationRequested()
        End If
    Next
End Sub

Ein vollständigeres Beispiel finden Sie unter Gewusst wie: Lauschen auf Abbruchanforderungen durch Abruf.For a more complete example, see How to: Listen for Cancellation Requests by Polling.

Lauschen durch Registrieren eines RückrufsListening by Registering a Callback

Einige Vorgänge können so blockiert werden, dass sie den Wert des Abbruchtokens nicht rechtzeitig überprüfen können.Some operations can become blocked in such a way that they cannot check the value of the cancellation token in a timely manner. In diesen Fällen können Sie eine Rückrufmethode registrieren, die die Blockierung der Methode aufhebt, wenn eine Abbruchanforderung empfangen wird.For these cases, you can register a callback method that unblocks the method when a cancellation request is received.

Die Register-Methode gibt ein CancellationTokenRegistration-Objekt zurück, das speziell für diesen Zweck verwendet wird.The Register method returns a CancellationTokenRegistration object that is used specifically for this purpose. Das folgende Beispiel zeigt, wie Sie die Register-Methode zum Abbrechen einer asynchronen Webanforderung verwenden.The following example shows how to use the Register method to cancel an asynchronous Web request.

using System;
using System.Net;
using System.Threading;

class Example
{
    static void Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        StartWebRequest(cts.Token);

        // cancellation will cause the web 
        // request to be cancelled
        cts.Cancel();
    }

    static void StartWebRequest(CancellationToken token)
    {
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => Console.WriteLine("Request completed.");

        // Cancellation on the token will 
        // call CancelAsync on the WebClient.
        token.Register(() =>
        {
            wc.CancelAsync();
            Console.WriteLine("Request cancelled!");
        });

        Console.WriteLine("Starting request.");
        wc.DownloadStringAsync(new Uri("http://www.contoso.com"));
    }
}
Imports System.Net
Imports System.Threading

Class Example
	Private Shared Sub Main()
		Dim cts As New CancellationTokenSource()

		StartWebRequest(cts.Token)

		' cancellation will cause the web 
		' request to be cancelled
		cts.Cancel()
	End Sub

	Private Shared Sub StartWebRequest(token As CancellationToken)
		Dim wc As New WebClient()
		wc.DownloadStringCompleted += Function(s, e) Console.WriteLine("Request completed.")

		' Cancellation on the token will 
		' call CancelAsync on the WebClient.
		token.Register(Function() 
		wc.CancelAsync()
		Console.WriteLine("Request cancelled!")

End Function)

		Console.WriteLine("Starting request.")
		wc.DownloadStringAsync(New Uri("http://www.contoso.com"))
	End Sub
End Class

Das CancellationTokenRegistration-Objekt verwaltet die Threadsynchronisierung und stellt sicher, dass die Ausführung des Rückrufs zu einem bestimmten Zeitpunkt beendet wird.The CancellationTokenRegistration object manages thread synchronization and ensures that the callback will stop executing at a precise point in time.

Um die Reaktionsfähigkeit des Systems sicherzustellen und Deadlocks zu vermeiden, müssen die folgenden Richtlinien beim Registrieren von Rückrufen beachtet werden:In order to ensure system responsiveness and to avoid deadlocks, the following guidelines must be followed when registering callbacks:

  • Die Rückrufmethode muss schnell sein, da sie synchron aufgerufen wird und der Aufruf von Cancel daher nicht zurückgegeben wird, bevor der Rückruf zurückgegeben wurde.The callback method should be fast because it is called synchronously and therefore the call to Cancel does not return until the callback returns.

  • Wenn Sie Dispose aufrufen, während der Rückruf ausgeführt wird, und Sie eine Sperre aufrechterhalten, auf die der Rückruf wartet, kann für das Programm ein Deadlock auftreten.If you call Dispose while the callback is running, and you hold a lock that the callback is waiting on, your program can deadlock. Nachdem Dispose zurückgegeben wurde, können Sie alle für den Rückruf erforderlichen Ressourcen freigeben.After Dispose returns, you can free any resources required by the callback.

  • Rückrufe sollten keine manuellen Threads durchführen oder SynchronizationContext in einem Rückruf verwenden.Callbacks should not perform any manual thread or SynchronizationContext usage in a callback. Wenn ein Rückruf für einen bestimmten Thread ausgeführt werden muss, verwenden Sie den System.Threading.CancellationTokenRegistration-Konstruktor, mit dem Sie angeben können, dass der Zielsynchronisierungskontext das aktive SynchronizationContext.Current ist.If a callback must run on a particular thread, use the System.Threading.CancellationTokenRegistration constructor that enables you to specify that the target syncContext is the active SynchronizationContext.Current. Manuelles Threading in einem Rückruf kann zu einem Deadlock führen.Performing manual threading in a callback can cause deadlock.

Ein vollständigeres Beispiel finden Sie unter Gewusst wie: Registrieren von Rückrufen für Abbruchanforderungen.For a more complete example, see How to: Register Callbacks for Cancellation Requests.

Lauschen mithilfe eines Wait-HandlesListening by Using a Wait Handle

Wenn ein abbrechbarer Vorgang blockiert werden kann, während er auf einen primitiven Synchronisierungstyp wie System.Threading.ManualResetEvent oder System.Threading.Semaphore wartet, können Sie die CancellationToken.WaitHandle-Eigenschaft verwenden, um es dem Vorgang zu ermöglichen, auf das Ereignis und die Abbruchanforderung zu warten.When a cancelable operation can block while it waits on a synchronization primitive such as a System.Threading.ManualResetEvent or System.Threading.Semaphore, you can use the CancellationToken.WaitHandle property to enable the operation to wait on both the event and the cancellation request. Das Wait-Handle des Abbruchtokens wird als Reaktion auf eine Abbruchanforderung signalisiert, und die Methode kann anhand des Rückgabewerts der WaitAny-Methode bestimmen, ob das Abbruchtoken signalisiert hat.The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. Der Vorgang kann dann einfach beenden oder ggf. eine OperationCanceledException auslösen.The operation can then just exit, or throw a OperationCanceledException, as appropriate.

// Wait on the event if it is not signaled.
int eventThatSignaledIndex =
       WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle },
                          new TimeSpan(0, 0, 20));
' Wait on the event if it is not signaled.
Dim waitHandles() As WaitHandle = { mre, token.WaitHandle }
Dim eventThatSignaledIndex =
    WaitHandle.WaitAny(waitHandles, _
                       New TimeSpan(0, 0, 20))

Im neuen Code, der auf .NET Framework 4.NET Framework 4, System.Threading.ManualResetEventSlim und System.Threading.SemaphoreSlim ausgerichtet ist, unterstützen beide das neue Abbruchframework in ihren Wait-Methoden.In new code that targets the .NET Framework 4.NET Framework 4, System.Threading.ManualResetEventSlim and System.Threading.SemaphoreSlim both support the new cancellation framework in their Wait methods. Sie können das CancellationToken an die Methode übergeben und bei der Abbruchanforderung wird das Ereignis reaktiviert, das ein OperationCanceledException auslöst.You can pass the CancellationToken to the method, and when the cancellation is requested, the event wakes up and throws an OperationCanceledException.

try
{
    // mres is a ManualResetEventSlim
    mres.Wait(token);
}
catch (OperationCanceledException)
{
    // Throw immediately to be responsive. The
    // alternative is to do one more item of work,
    // and throw on next iteration, because
    // IsCancellationRequested will be true.
    Console.WriteLine("The wait operation was canceled.");
    throw;
}

Console.Write("Working...");
// Simulating work.
Thread.SpinWait(500000);
Try
   ' mres is a ManualResetEventSlim
    mres.Wait(token)
Catch e As OperationCanceledException
    ' Throw immediately to be responsive. The
    ' alternative is to do one more item of work,
    ' and throw on next iteration, because
    ' IsCancellationRequested will be true.
    Console.WriteLine("Canceled while waiting.")
    Throw
End Try

 ' Simulating work.
Console.Write("Working...")
Thread.SpinWait(500000)

Ein vollständigeres Beispiel finden Sie unter Gewusst wie: Lauschen auf Abbruchanforderungen mit Wait-Handles.For a more complete example, see How to: Listen for Cancellation Requests That Have Wait Handles.

Gleichzeitiges Lauschen auf mehrere TokenListening to Multiple Tokens Simultaneously

In einigen Fällen muss ein Listener möglicherweise auf mehrere Abbruchtoken gleichzeitig lauschen.In some cases, a listener may have to listen to multiple cancellation tokens simultaneously. Ein abbrechbarer Vorgang muss z. B. möglicherweise zusätzlich zu einem extern als Argument an einen Methodenparameter übergebenen Token ein internes Abbruchtoken überwachen.For example, a cancelable operation may have to monitor an internal cancellation token in addition to a token passed in externally as an argument to a method parameter. Zu diesem Zweck erstellen Sie eine verknüpfte Tokenquelle, die zwei oder mehr Token zu einem Token verbinden kann, wie im folgenden Beispiel gezeigt.To accomplish this, create a linked token source that can join two or more tokens into one token, as shown in the following example.

public void DoWork(CancellationToken externalToken)
{
   // Create a new token that combines the internal and external tokens.
   this.internalToken = internalTokenSource.Token;
   this.externalToken = externalToken;

   using (CancellationTokenSource linkedCts =
           CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken))
   {
       try {
           DoWorkInternal(linkedCts.Token);
       }
       catch (OperationCanceledException) {
           if (internalToken.IsCancellationRequested) {
               Console.WriteLine("Operation timed out.");
           }
           else if (externalToken.IsCancellationRequested) {
               Console.WriteLine("Cancelling per user request.");
               externalToken.ThrowIfCancellationRequested();
           }
       }
   }
}
Public Sub DoWork(ByVal externalToken As CancellationToken)
   ' Create a new token that combines the internal and external tokens.
   Dim internalToken As CancellationToken = internalTokenSource.Token
   Dim linkedCts As CancellationTokenSource =
   CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)
   Using (linkedCts)
      Try
         DoWorkInternal(linkedCts.Token)
      Catch e As OperationCanceledException
         If e.CancellationToken = internalToken Then
            Console.WriteLine("Operation timed out.")
         ElseIf e.CancellationToken = externalToken Then
            Console.WriteLine("Canceled by external token.")
            externalToken.ThrowIfCancellationRequested()
         End If
      End Try
   End Using
End Sub

Beachten Sie, dass Sie Dispose für die verknüpfte Tokenquelle aufrufen müssen, wenn Sie damit fertig sind.Notice that you must call Dispose on the linked token source when you are done with it. Ein vollständigeres Beispiel finden Sie unter Gewusst wie: Lauschen auf mehrere Abbruchanforderungen.For a more complete example, see How to: Listen for Multiple Cancellation Requests.

Kooperation zwischen Bibliothekscode und BenutzercodeCooperation Between Library Code and User Code

Das einheitliche Abbruchframework ermöglicht es dem Bibliothekscode, den Benutzercode abzubrechen. Ebenso ermöglicht es dem Benutzercode, den Bibliothekscode auf kooperative Weise abzubrechen.The unified cancellation framework makes it possible for library code to cancel user code, and for user code to cancel library code in a cooperative manner. Die problemlose Zusammenarbeit hängt davon ab, ob die beiden Seiten die folgenden Richtlinien beachten:Smooth cooperation depends on each side following these guidelines:

  • Wenn der Bibliothekscode abbrechbare Vorgänge bereitstellt, sollten auch öffentliche Methoden bereitgestellt werden, die ein externes Abbruchtoken akzeptieren, damit der Benutzercode den Abbruch anfordern kann.If library code provides cancelable operations, it should also provide public methods that accept an external cancellation token so that user code can request cancellation.

  • Wenn der Bibliothekscode einen Aufruf innerhalb des Benutzercodes ausführt, sollte der Bibliothekscode ein OperationCanceledException(externalToken) als kooperativen Abbruch und nicht notwendigerweise als Fehlerausnahme interpretieren.If library code calls into user code, the library code should interpret an OperationCanceledException(externalToken) as cooperative cancellation, and not necessarily as a failure exception.

  • Benutzerdelegaten sollten versuchen, zeitnah auf Abbruchanforderungen aus Bibliothekscode zu reagieren.User-delegates should attempt to respond to cancellation requests from library code in a timely manner.

System.Threading.Tasks.Task und System.Linq.ParallelEnumerable sind Beispiele für Klassen, die diese Richtlinien einhalten.System.Threading.Tasks.Task and System.Linq.ParallelEnumerable are examples of classes that follows these guidelines. Weitere Informationen finden Sie unter Aufgabenabbruch und Gewusst wie: Abbrechen einer PLINQ-Abfrage.For more information, see Task Cancellationand How to: Cancel a PLINQ Query.

Siehe auchSee Also

Grundlagen des verwalteten ThreadingsManaged Threading Basics