我的延续任务似乎在完成之前就已完成 运行

My continuation task appears to be complete before it finishes running

我有一个关于延续任务的非直觉问题,我认为 task.Wait 会等待延续任务,但它在任务完成之前 RanToCompletion 产生 运行ning ?这是简短的源代码。输出如下:

private static void TestChildTasks()
{
    Task t = Task.Run(() => RunParentTask());
    Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));
    //Task t2 = Task.Run(() => RunChildTask());
    Console.WriteLine("Waiting on t1");
    t.Wait();
    Console.WriteLine("Done waiting on t1");

    Console.WriteLine($"Waiting on t2, status of {t2.Status}");
    t2.Wait();

    Console.WriteLine($"Finished; child task is {t2.Status}");
}

private static void RunParentTask()
{
    Console.WriteLine("Parent Task is running");
    Thread.Sleep(2000);
    Console.WriteLine("Parent Task is done");
}

private static void RunChildTask()
{
    Console.WriteLine("Child task is running");
    Thread.Sleep(3000);
    Console.WriteLine("Child Task is done");
}

这是输出:

Waiting on t1

Parent Task is running

Parent Task is done

Done waiting on t1

Waiting on t2, status of Running

Finished; child task is RanToCompletion

press enter to exit

Child task is running

Child Task is done

为什么子任务在 returns 之后继续 运行 RanToCompletion 的状态?

让我们看看下面一行:

Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));

尽管您已将 t2 声明为 Task,但它实际上是 Task<Task>:

Task<Task> t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));

为什么?因为 Task.Run 创建了一个新的 Task.

您有多种选择可以解决此问题:

选项 #1 - 展开

要从 Task<Task> 生成 Task,您需要调用 Unwrap 方法。

Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask())).Unwrap();

经过此修改,输出将如下所示:

Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Child task is running
Waiting on t2, status of WaitingForActivation
Child Task is done
Finished; child task is RanToCompletion

选项 #2 - 避免创建新任务

实际上你可以在没有 Task.Run 的情况下调用 RunChildTask:

Task t2 = t.ContinueWith(_ => RunChildTask());

经过此修改,输出将如下所示:

Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Child task is running
Waiting on t2, status of Running
Child Task is done
Finished; child task is RanToCompletion

选项 #3 - AttachToParent

如果您愿意,您不仅可以使用 ContinueWith 附加任务,还可以使用 Task.Factory.StartNew:

Task t2 = null;
Task t = Task.Factory.StartNew(() =>
{
    RunParentTask();
    t2 = Task.Factory.StartNew(RunChildTask, TaskCreationOptions.AttachedToParent);
});

经过此修改,输出将如下所示:

Waiting on t1
Parent Task is running
Parent Task is done
Child task is running
Child Task is done
Done waiting on t1
Waiting on t2, status of RanToCompletion
Finished; child task is RanToCompletion

还有其他几种方法。