【问题标题】:Is there a way I can cause a running method to stop immediately with a cts.Cancel();有没有办法可以通过 cts.Cancel(); 立即停止正在运行的方法?
【发布时间】:2019-12-09 05:49:04
【问题描述】:

我有创建 CancellationTokenSource 并将其传递给方法的代码。

我在另一个应用程序中的代码发出 cts.Cancel();

有没有一种方法可以使该方法立即停止,而无需等待 while 循环中的两行完成?

请注意,如果它导致我可以处理的异常,我会没事的。

public async Task OnAppearing()
{
   cts = new CancellationTokenSource();
   await GetCards(cts.Token);
}

public async Task GetCards(CancellationToken ct)
{
   while (!ct.IsCancellationRequested)
   {
      App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts);
      await CheckAvailability();
   }
}

【问题讨论】:

  • ct.ThrowIfCancellationRequested?
  • 合作取消。它所取代的 模型是threads 和thread.abort,它可以满足您的要求,但很难正确使用(在一般情况下),因此大部分都被放弃了。
  • @Damien_The_Unbeliever Thread.Abort 未在 .NET Core 中实现。但是,是的,它可以在 .NET Framework 中运行。

标签: c# multithreading task cancellationtokensource


【解决方案1】:

我可以建议:

  1. 修改 GetViewablePhrases 和 CheckAvailability,以便您可以将 CancellationToken 传递给它们;
  2. 在这些函数中使用ct.ThrowIfCancellationRequested()
  3. 尝试/捕获 GetCards 中的 OperationCanceledException;

至于您的功能,我不知道它们在内部是如何工作的。但是让我们假设您在其中一个内部有一个长时间运行的迭代:

CheckAvailability(CancellationToken ct)
{
    for(;;) 
    {
        // if cts.Cancel() was executed - this method throws the OperationCanceledException
        // if it wasn't the method does nothing
        ct.ThrowIfCancellationRequested(); 
        ...calculations... 
    } 
}

或者假设您要在其中一个函数中访问您的数据库,并且您知道此过程需要一段时间:

CheckAvailability(CancellationToken ct)
{
    ct.ThrowIfCancellationRequested();
    AccessingDatabase();
}

这不仅会阻止您的函数继续执行,还会将执行者任务状态设置为 TaskStatus.Canceled

别忘了捕捉异常:

public async Task GetCards(CancellationToken ct)
{
   try
   {
      App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts, ct);
      await CheckAvailability(ct);
   }
   catch(OperationCanceledException ex)
   {
       // handle the cancelation...
   }
   catch
   {
       // handle the unexpected exception
   }
}

【讨论】:

  • 您能举个例子说明我将如何在函数中使用 ct.ThrowIfCancellationRequested。一个非常简单的例子就会有所帮助。
  • @Alan2 我为你稍微修改了我的答案。我希望它有所帮助。
【解决方案2】:

如果您可以不取消任务,而是等待任务,您可以使用可取消包装器。如果取消,底层任务将继续运行,但包装器将在取消后立即完成。

public static Task AsCancelable(this Task task,
    CancellationToken cancellationToken)
{
    var cancelable = new Task(() => { }, cancellationToken);
    return Task.WhenAny(task, cancelable).Unwrap();
}

public static Task<T> AsCancelable<T>(this Task<T> task,
    CancellationToken cancellationToken)
{
    var cancelable = new Task<T>(() => default, cancellationToken);
    return Task.WhenAny(task, cancelable).Unwrap();
}

使用示例:

await GetCards(cts.Token).AsCancelable(cts.Token);

此扩展方法也可以使用TaskCompletionSource&lt;T&gt;(而不是Task&lt;T&gt; 构造函数)来实现。

【讨论】:

    猜你喜欢
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 2014-10-22
    • 2013-12-25
    • 1970-01-01
    • 2021-01-14
    • 2014-08-18
    相关资源
    最近更新 更多