Export (0) Print
Expand All
This topic has not yet been rated - Rate this topic

How to: Listen for Multiple Cancellation Requests

This example shows how to listen to two cancellation tokens simultaneously so that you can cancel an operation if either token requests it.

Note Note

When "Just My Code" is enabled, Visual Studio in some cases will break on the line that throws the exception and display an error message that says "exception not handled by user code." This error is benign. You can press F5 to continue from it, and see the exception-handling behavior that is demonstrated in the examples below. To prevent Visual Studio from breaking on the first error, just uncheck the "Just My Code" checkbox under Tools, Options, Debugging, General.

In the following example, the CreateLinkedTokenSource method is used to join two tokens into one token. This enables the token to be passed to methods that take just one cancellation token as an argument. The example demonstrates a common scenario in which a method must observe both a token passed in from outside the class, and a token generated inside the class.

Class LinkedTokenSourceDemo

    Shared Sub Main()

      Dim worker As New WorkerWithTimer()
      Dim cts As New CancellationTokenSource()

      ' Task for UI thread, so we can call Task.Wait wait on the main thread.
      Task.Run(Sub()
                  Console.WriteLine("Press 'c' to cancel within 3 seconds after work begins.")
                  Console.WriteLine("Or let the task time out by doing nothing.")
                  If Console.ReadKey().KeyChar = "c"c Then
                     cts.Cancel()
                  End If 
               End Sub
)
        ' Let the user read the UI message.
        Thread.Sleep(1000)

        ' Start the worker task. 
      Dim t As Task = Task.Run(Sub() worker.DoWork(cts.Token), cts.Token)

        Try

            t.Wait()


        Catch ae As AggregateException

            For Each inner In ae.InnerExceptions
                Console.WriteLine(inner.Message)
            Next 
        End Try

        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub 
End Class 

Class WorkerWithTimer

    Dim internalTokenSource As CancellationTokenSource
    Dim token As CancellationToken
    Dim myTimer As Timer

    Public Sub WorkerWithTimer()

        internalTokenSource = New CancellationTokenSource()
        token = internalTokenSource.Token

        ' A toy cancellation trigger that times out after 3 seconds 
        ' if the user does not press 'c'.
        myTimer = New Timer(New TimerCallback(AddressOf CancelAfterTimeout), Nothing, 3000, 3000)
    End Sub 


    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 


    Private Sub DoWorkInternal(ByVal token As CancellationToken)

        For i As Integer = 0 To 1000

            If token.IsCancellationRequested Then 

                ' We need to dispose the timer if cancellation 
                ' was requested by the external token.
                myTimer.Dispose()

                ' Output for demonstration purposes.
                Console.WriteLine("\r\nCancelling per request.")

                ' Throw the exception.
                token.ThrowIfCancellationRequested()
            End If 

            ' Simulating work.
            Thread.SpinWait(7500000)
            Console.Write("working... ")
        Next 
    End Sub 

    Public Sub CancelAfterTimeout(ByVal state As Object)

        Console.WriteLine("\r\nTimer fired.")
        internalTokenSource.Cancel()
        myTimer.Dispose()
    End Sub 
End Class

When the linked token throws an OperationCanceledException, the token that is passed to the exception is the linked token, not either of the predecessor tokens. To determine which of the tokens was canceled, check the status of the predecessor tokens directly.

In this example, AggregateException should never be thrown, but it is caught here because in real-world scenarios any other exceptions besides OperationCanceledException that are thrown from the task delegate are wrapped in a OperationCanceledException.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.