【问题标题】:Child task canceled, Parent completed?子任务取消,父任务完成?
【发布时间】:2015-02-17 14:47:30
【问题描述】:

我正在尝试了解 .net 任务的行为,当孩子被附加时。

我有以下测试代码:

void Test()
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;

    Task child = null;
    var parent = Task.Factory.StartNew(() =>
    {
        child = Task.Factory.StartNew(() =>
        {
            while (!token.IsCancellationRequested)
                Thread.Sleep(100);
            token.ThrowIfCancellationRequested();
        }, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
    }, token);

    Thread.Sleep(500);

    Debug.WriteLine("State of parent before cancel is {0}", parent.Status);
    Debug.WriteLine("State of child before cancel is {0}", child.Status);

    tokenSource.Cancel();
    Thread.Sleep(500);

    Debug.WriteLine("State of parent is {0}", parent.Status);
    Debug.WriteLine("State of child is {0}", child.Status);
}

这个结果是:

State of parent before cancel is WaitingForChildrenToComplete
State of child before cancel is Running
A first chance exception of type 'System.OperationCanceledException' occurred in mscorlib.dll
State of parent is RanToCompletion
State of child is Canceled

显然父任务状态不是Canceled,即使 两个任务共享令牌,并附加孩子。

发生取消时如何使父任务返回状态Canceled

注意 如果我抛出异常,两个任务都会返回Faulted

【问题讨论】:

  • Faulted 是这里的特殊状态,其中(无论出于何种原因)孩子的状态反映在父母身上。 Cancelled 特别是 called out 为“要以取消状态结束,任务必须在开始执行之前请求取消,或者必须在执行期间确认取消请求。”并且这些都不适用于父母。
  • 所以父任务应该等待子任务,并抛出它自己的OperationCanceledException 以取消其状态?

标签: c# task-parallel-library


【解决方案1】:

这是 MSDN 中所述的预期行为。子任务的父任务必须wait(向下滚动到取消部分)。父任务必须处理所有良性故障(如取消)。

要让你的父任务失败,只需等待并传递令牌:

Task child = null;
var parent = Task.Factory.StartNew(() =>
{
  child = Task.Factory.StartNew(() =>
  {
    while (!token.IsCancellationRequested) Thread.Sleep(100);
    token.ThrowIfCancellationRequested();
  }, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);

  // This is the magic line.
  child.Wait(token);
}, token);

如果您使用此代码来做一些富有成效的事情而不仅仅是为了测试,您还应该考虑使用简化的Task.Run(),它支持async 代表而不是Task.Factory.StartNew()。这个article很有意思。

【讨论】:

  • 在哪里记录了这是预期的行为?我找不到它。
  • 查看我在回答中添加的 MSDN 链接。
  • 我看了那个链接,还是找不到。它说您必须等待父任务处理异常,而不是您在回答中所说的子任务。你能引用确切的文字是什么意思吗?
  • @SriramSakthivel 不是说等待for父任务,而是on:“等待on 父母”
  • 算了。没关系。你在哪里读到你必须等待子任务?我找不到它。看起来文档没有说出来。
【解决方案2】:

您的示例非常复杂,隐藏了直观的行为,您的期望是错误的。

让我们从工作示例开始:

void Test()
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;

    Task child = null;
    var parent = Task.Factory.StartNew(() =>
    {
        child = Task.Factory.StartNew(() =>
        {
            while (!token.IsCancellationRequested)
                Thread.Sleep(100);
            token.ThrowIfCancellationRequested();
        }, token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);

        while (!token.IsCancellationRequested)
            Thread.Sleep(100);
        token.ThrowIfCancellationRequested();
    }, token);

    Thread.Sleep(500);

    Debug.WriteLine("State of parent before cancel is {0}", parent.Status);
    Debug.WriteLine("State of child before cancel is {0}", child.Status);

    tokenSource.Cancel();
    Thread.Sleep(500);

    Debug.WriteLine("State of parent is {0}", parent.Status);
    Debug.WriteLine("State of child is {0}", child.Status);
}

为了取消父级,您需要调用父级 token.ThrowIfCancellationRequested() 的正文中的某个位置。然而,仅仅调用token.ThrowIfCancellationRequested() 是不够的。

您需要概念化 parentchild 执行流程如何协同工作,以了解您的预期错误的原因。

Main thread: ---\------------------------------------[Cancel]-----/
Parent:          \---\-----[Check cancellation]------------------/
Child:                \------------------------------[Cancel]---/

从上图中可以看出,父级在请求取消之前检查取消方式。孩子收到取消信号,因为它基本上等待取消被触发。现在,如果您将相同的机制放在父级中,它将收到取消信号,因为它不会在发出取消信号之前完成它的工作。

【讨论】:

    【解决方案3】:

    当附加的子任务取消时

    文档的standard version 声明您需要等待父任务。

    当我尝试在主线程中等待 parentTask.Wait() 时 - 没有错误。

    old one'等待父任务'。

    当我尝试在 parentTask 中等待 childTask.Wait(),然后在主线程中等待 parentTask.Wait() - 我遇到了错误。

    因此,当前的文档具有误导性。 另一方面,默认情况下,父任务应等待所有附加的子任务。所以我不明白为什么要显式等待 parentTask 中的 childTask.Wait() 以在主线程中捕获 TaskCanceledException。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-07
      相关资源
      最近更新 更多