如何链接独立的C#任务?

Pet*_*aer 6 c# task-parallel-library

假设我有两个创建Task对象的独立异步函数(我无法控制):

Task A();
Task B();
Run Code Online (Sandbox Code Playgroud)

和其他一些非异步功能

void X();
Run Code Online (Sandbox Code Playgroud)

如何构建一个单独的任务链,按顺序执行所有这些,并允许附加继续(将在X之后执行)?

如果我这样做:

Task Sequential()
{
   return A()
     .ContinueWith(t => { B(); })
     .ContinueWith(t => { X(); });
}
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为B将启动一个新的任务链.如果B需要很长时间才能完成,则首先执行X(以及在返回的Task上,Sequential的调用者可能会继续执行任何其他操作).我需要X作为单个任务链中的最后一个元素,以B任务为先例.

如果我这样做:

Task Sequential()
{
   return A()
     .ContinueWith(t => { B().ContinueWith(t => { X(); }); });
}
Run Code Online (Sandbox Code Playgroud)

这只能部分解决问题,因为即使A,B和X现在按顺序执行,如果调用者执行Sequential().ContinueWith,该延续将并行执行到B和X,而不是X.

Ale*_*ung 7

有没有理由不使用等待?例如,

async Task Sequential()
{
    await A();
    await B();
    X();
}
Run Code Online (Sandbox Code Playgroud)


Kir*_*kiy 5

假设您不能按照其他答案中的建议使用(如果可以,您应该),自从.NET 4.0 中async/await引入以来,有一个漂亮的小扩展方法可以满足这种情况: 。它接受 a (或) 并将其“展平”为连续的(或分别),这表示外部任务和内部任务的完成。TaskSystem.Threading.Tasks.TaskExtensions.UnwrapTask<Task>Task<Task<TResult>>TaskTask<TResult>

使用该扩展方法,您的方法可以重写为:

Task Sequential()
{
    return A()
        .ContinueWith(t => B()).Unwrap()
        .ContinueWith(t => X()); // You said X() is "non-async", so no Unwrap here.
}
Run Code Online (Sandbox Code Playgroud)

结果Task将代表整个顺序任务链按预期顺序完成。

还有“子任务”的概念,最初是在任务并行库的早期为此目的而设计的,但它非常难以使用,并且需要您对任务的启动方式有很好的控制您可以没有。尽管如此,它仍然值得了解(如果只是为了教育)。