【问题标题】:Cannot cancel a task via CancellationTokenSource.Cancel()无法通过 CancellationTokenSource.Cancel() 取消任务
【发布时间】:2013-06-06 16:25:11
【问题描述】:

我无法停止任务。首先,我刚开始使用Tasks。以前,我一直使用 delegate.BeginInvoke() 在后台运行,但这次我需要停止后台执行,如果需要的话。所以我切换到任务。这是我的代码:

CancellationTokenSource token = new CancellationTokenSource();
Task posting = Task.Factory.StartNew(() => DoPosting(docs, session), token.Token);
HttpRuntime.Cache[session.ToString() + "_token"] = token;
HttpRuntime.Cache[session.ToString() + "_task"] = posting;

这是 ASP.NET MVC,所以我将持久的东西存储在 HttpRuntime.Cache 中。用户可以通过此操作取消操作:

public JsonResult StopPosting(string session)
{
    CancellationTokenSource token = (CancellationTokenSource)HttpRuntime.Cache.Get(session.ToString() + "_token");
    Task posting = (Task)HttpRuntime.Cache[session.ToString() + "_task"];
    token.Cancel();

    return Json(new { Message = "Stopped!" });
}

现在,当这个动作第一次被击中时,什么也没有发生。然后我第二次要求取消。现在,token.IsCancellationRequested 说“真”,所以 token.Cancel() 一定做了什么。但是posting.Status 仍然是“正在运行”。它将保持这种状态,直到它完成,然后它是“RunToCompletion”。

所以,我请求取消一个任务,但它没有被取消。

也许我做错了什么,或者我遗漏了一些明显的东西,但我无法看到/理解为什么它不会取消。

也许有人可以解释一下?

问候。

【问题讨论】:

  • 一点也不。它对数据库执行繁重的工作。我知道我应该注意那里发生的事情,但我无法克服第一个障碍。感谢您的链接。

标签: c# multithreading task-parallel-library task


【解决方案1】:

取消令牌不会立即导致任务的委托在原处停止执行。支持这一点(就像通过线程通过Abort 完成的那样)可能会导致各种不良情况,包括未正确清理对象、由于执行在逻辑上应该被观察为原子的操作中途停止而违反不变量等。

您需要做的是让正在执行的实际函数查看 CancellationToken 并定期验证它是否未被取消。除了将令牌传递给StartNew,您还需要将它传递给DoPosting 方法。然后,该方法需要定期调用 token.ThrowIfCancellationRequested(); 在方法中如果确实请求取消时应该停止的地方。

请参阅How do I cancel non-cancelable async operations? 了解更多信息。

【讨论】:

  • 好的,我明白你在说什么。会那样做。但是,我想到了一个问题。无论如何,什么是好的令牌,我可以让 DoPosting 在某处检查标志,并得到相同的结果。只是想看看取消令牌有什么好处?
  • @MilosMijatovic 有几个因素。首先是处理同步问题。 CancellationToken 只不过是一个布尔值的包装器,但它使用适当的内存屏障等来确保可以从不同的线程正确地观察/改变它。接下来,它允许Task 区分被取消和刚刚正常完成或抛出异常,这可以让继续对取消的任务采取不同的行动(通常很有用)。最后,它允许在 Task 开始之前将其标记为已取消。
  • 谢谢。好点。我肯定需要更多阅读有关该主题的内容。
【解决方案2】:

我认为您可能对 CancellationToken 期望过高。至于任务,我认为 CancellationToken 仅在确定是否在任务可用插槽时启动任务时使用。一旦任务启动,任务框架就不能在取消 CancellationToken 时简单地中止委托调用。为了完全取消这样的事情,必须编写由任务框架 (DoPosting) 调用的代码以了解 CancellationToken,并且必须直接在代码中的适当位置检查该令牌的状态。

【讨论】:

  • 感谢您的意见 Michael,也。我期待的太多了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多