返回介绍

如何:侦听多个取消请求

发布于 2025-02-23 23:16:12 字数 6958 浏览 0 评论 0 收藏 0

此示例演示如何同时侦听除外到两个取消标记,以便你可以取消的操作,如果任一令牌请求,则。

备注

某些情况下,当启用 仅我的代码 后,Visual Studio 会在引发异常的行中断运行并显示一条错误消息,该消息显示 用户代码未处理异常 。 此错误是良性的。 可以按 F5 继续运行,并请参阅下面示例中所示的异常处理行为。 若要阻止 Visual Studio 的第一个错误时中断,只需取消选中下的"仅我的代码"复选框 工具、 选项、 调试、 常规

示例

在下面的示例中, CreateLinkedTokenSource 方法用于将两个标记联接成一个令牌。 这使得可以将标记传递给只采用一个取消标记作为参数的方法。 该示例演示一种方法必须在其中观察在传递从外部类,并在类中生成的令牌的这两个标记的常见方案。

using System;
using System.Threading;
using System.Threading.Tasks;

class LinkedTokenSourceDemo
{
  static void Main()
  {
    WorkerWithTimer worker = new WorkerWithTimer();
    CancellationTokenSource cts = new CancellationTokenSource();

    // Task for UI thread, so we can call Task.Wait wait on the main thread.
    Task.Run(() =>
    {
      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(true).KeyChar == 'c')
        cts.Cancel();
    });

    // Let the user read the UI message.
    Thread.Sleep(1000);

    // Start the worker task.
    Task task = Task.Run(() => worker.DoWork(cts.Token), cts.Token);

    try {
      task.Wait(cts.Token);
    }
    catch (OperationCanceledException e) {
      if (e.CancellationToken == cts.Token)
        Console.WriteLine("Canceled from UI thread throwing OCE.");
    }
    catch (AggregateException ae) {
      Console.WriteLine("AggregateException caught: " + ae.InnerException);
      foreach (var inner in ae.InnerExceptions) {
        Console.WriteLine(inner.Message + inner.Source);
      }
    }

    Console.WriteLine("Press any key to exit.");
    Console.ReadKey();
    cts.Dispose();
  }
}

class WorkerWithTimer
{
  CancellationTokenSource internalTokenSource = new CancellationTokenSource();
  CancellationToken internalToken;
  CancellationToken externalToken;
  Timer timer;

  public WorkerWithTimer()
  {
    internalTokenSource = new CancellationTokenSource();
    internalToken = internalTokenSource.Token;

    // A toy cancellation trigger that times out after 3 seconds
    // if the user does not press 'c'.
    timer = new Timer(new TimerCallback(CancelAfterTimeout), null, 3000, 3000);
  }

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

   private void DoWorkInternal(CancellationToken token)
   {
    for (int i = 0; i < 1000; i++)
    {
      if (token.IsCancellationRequested)
      {
        // We need to dispose the timer if cancellation
        // was requested by the external token.
        timer.Dispose();

        // Throw the exception.
        token.ThrowIfCancellationRequested();
      }

       // Simulating work.
      Thread.SpinWait(7500000);
      Console.Write("working... ");
    }
   }

   public void CancelAfterTimeout(object state)
   {
    Console.WriteLine("\r\nTimer fired.");
    internalTokenSource.Cancel();
    timer.Dispose();
   }
}
Imports System.Threading
Imports System.Threading.Tasks

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(True).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()
    cts.Dispose()
  End Sub
End Class

Class WorkerWithTimer
   Dim internalTokenSource As CancellationTokenSource
   Dim token As CancellationToken
   Dim timer As Timer

   Public Sub New()
    internalTokenSource = New CancellationTokenSource()
    token = internalTokenSource.Token

    ' A toy cancellation trigger that times out after 3 seconds
    ' if the user does not press 'c'.
    timer = 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.
       timer.Dispose()

      ' Output for demonstration purposes.
      Console.WriteLine(vbCrLf + "Cancelling 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(vbCrLf + "Timer fired.")
    internalTokenSource.Cancel()
    timer.Dispose()
   End Sub
End Class

当链接的令牌引发 OperationCanceledException ,传递给此异常的标记是链接的令牌,不是任何一个前置令牌。 若要确定哪些标记已取消,请直接检查前置标记的状态。

在此示例中, AggregateException 应永远不会引发,但因为此处捕获在实际方案中除以外的任何其他异常 OperationCanceledException 从任务委托引发的包装在 OperationCanceledException 。

另请参阅

托管线程中的取消

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文