Freigeben über


Gewusst wie: Lauschen auf Abbruchanforderungen durch Abruf

Im folgenden Beispiel wird eine Methode veranschaulicht, wie in Benutzercode ein Abbruchtoken abgefragt werden kann, um in regelmäßigen Abständen zu überprüfen, ob vom aufrufenden Thread ein Abbruch angefordert wurde. In diesem Beispiel wird der System.Threading.Tasks.Task-Typ verwendet, das gleiche Muster gilt jedoch auch für asynchrone Vorgänge, die direkt vom System.Threading.ThreadPool-Typ oder System.Threading.Thread-Typ erstellt werden.

Beispiel

Für das Abrufen ist Code mit Schleifen oder Rekursionen erforderlich, der regelmäßig den Wert der booleschen IsCancellationRequested-Eigenschaft lesen kann. Wenn Sie den System.Threading.Tasks.Task-Typ verwenden und darauf warten, dass die Aufgabe für den aufrufenden Thread abgeschlossen wird, können Sie mithilfe der ThrowIfCancellationRequested-Methode die Eigenschaft überprüfen und die Ausnahme auslösen. Mit dieser Methode wird sichergestellt, dass die richtige Ausnahme als Reaktion auf eine Anforderung ausgelöst wird. Wenn Sie einen Task verwenden, ist das Aufrufen dieser Methode dem manuellen Auslösen einer OperationCanceledException vorzuziehen. Wenn Sie die Ausnahme nicht auslösen müssen, dann können Sie die Eigenschaft und Rückgabe der Methode einfach überprüfen, wenn die Eigenschaft true ist.

Class CancelByPolling

    Shared Sub Main()

        Dim tokenSource As New CancellationTokenSource()
        ' Toy object for demo purposes
        Dim rect As New Rectangle()
        rect.columns = 1000
        rect.rows = 500

        ' Simple cancellation scenario #1. Calling thread does not wait
        ' on the task to complete, and the user delegate simply returns
        ' on cancellation request without throwing.
        Task.Factory.StartNew(Sub() NestedLoops(rect, tokenSource.Token), tokenSource.Token)

        ' Simple cancellation scenario #2. Calling thread does not wait
        ' on the task to complete, and the user delegate throws 
        ' OperationCanceledException to shut down task and transition its state.
        ' Task.Factory.StartNew(Sub() PollByTimeSpan(tokenSource.Token), tokenSource.Token)

        Console.WriteLine("Press 'c' to cancel")
        If Console.ReadKey().KeyChar = "c"c Then

            tokenSource.Cancel()
            Console.WriteLine("Press any key to exit.")
        End If

        Console.ReadKey()

    End Sub
    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("\r\nCancelling 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



End Class
class CancelByPolling
{
    static void Main()
    {
        var tokenSource = new CancellationTokenSource();
        // Toy object for demo purposes
        Rectangle rect = new Rectangle() { columns = 1000, rows = 500 };

        // Simple cancellation scenario #1. Calling thread does not wait
        // on the task to complete, and the user delegate simply returns
        // on cancellation request without throwing.
        Task.Factory.StartNew(() => NestedLoops(rect, tokenSource.Token), tokenSource.Token);

        // Simple cancellation scenario #2. Calling thread does not wait
        // on the task to complete, and the user delegate throws 
        // OperationCanceledException to shut down task and transition its state.
        // Task.Factory.StartNew(() => PollByTimeSpan(tokenSource.Token), tokenSource.Token);

        Console.WriteLine("Press 'c' to cancel");
        if (Console.ReadKey().KeyChar == 'c')
        {
            tokenSource.Cancel();
            Console.WriteLine("Press any key to exit.");
        }

        Console.ReadKey();

    }
    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();
            }
        }
    }
}

Das Aufrufen von ThrowIfCancellationRequested ist äußerst schnell und führt nicht zu bedeutendem Mehraufwand durch Schleifen.

Wenn Sie ThrowIfCancellationRequested aufrufen, müssen Sie die IsCancellationRequested-Eigenschaft nur dann explizit überprüfen, wenn als Reaktion auf den Abbruch weitere Reaktionen als das Auslösen der Ausnahme erforderlich sind. In diesem Beispiel können Sie sehen, dass der Code eigentlich zweimal auf die Eigenschaft zugreift: einmal explizit und dann mit der ThrowIfCancellationRequested-Methode. Da jedoch für das Lesen der IsCancellationRequested-Eigenschaft nur eine flüchtige read-Anweisung pro Zugriff erforderlich ist, ist der zweifache Zugriff im Hinblick auf die Leistung unerheblich. Das Aufrufen der Methode ist weiterhin dem manuellen Auslösen der OperationCanceledException vorzuziehen.

Siehe auch

Konzepte

Abbruch