【发布时间】:2016-06-03 17:36:16
【问题描述】:
如果我的代码通过返回代表每个阶段的任务来抽象异步操作的分阶段序列,我如何确保继续按阶段顺序(即任务完成的顺序)执行?
请注意,这与简单的“不要浪费时间等待较慢的任务”不同。在调度中需要保证没有竞争条件的顺序。这个较宽松的要求可以通过以下问题的部分答案来解决:
- Sort Tasks into order of completition
- Is there default way to get first task that finished successfully?
我认为合乎逻辑的解决方案是使用自定义 TaskScheduler(例如基于 SynchronizationContext 的)附加延续。但是,我无法保证调度在任务完成时同步执行。
在代码中,这可能类似于
class StagedOperationSource
{
public TaskCompletionSource Connect = new TaskCompletionSource();
public TaskCompletionSource Accept = new TaskCompletionSource();
public TaskCompletionSource Complete = new TaskCompletionSource();
}
class StagedOperation
{
public Task Connect, Accept, Complete;
public StagedOperation(StagedOperationSource source)
{
Connect = source.Connect.Task;
Accept = source.Accept.Task;
Complete = source.Complete.Task;
}
}
...
private StagedOperation InitiateStagedOperation(int opId)
{
var source = new StagedOperationSource();
Task.Run(GetRunnerFromOpId(opId, source));
return new StagedOperation(source);
}
...
public RunOperations()
{
for (int i=0; i<3; i++)
{
var op = InitiateStagedOperation(i);
op.Connect.ContinueWith(t => Console.WriteLine("{0}: Connected", i));
op.Accept.ContinueWith(t => Console.WriteLine("{0}: Accepted", i));
op.Complete.ContinueWith(t => Console.WriteLine("{0}: Completed", i));
}
}
应该产生类似于
的输出0: Connected
1: Connected
0: Accepted
2: Connected
0: Completed
1: Accepted
2: Accepted
2: Completed
1: Completed
显然,该示例缺少详细信息,例如如果早期阶段失败,则将异常转发到(或取消)后期阶段,但这只是一个示例。
【问题讨论】:
-
您可能想查看TPL Dataflow
-
您可以使用像 waitone 这样的信号量或使用锁来确保一个任务在下一个任务开始之前完成。
-
@jdweng 那不会是异步的。为此,您必须为循环的每次迭代创建一个线程和信号量,从而使代码远远比它需要的更昂贵。
-
不能完全异步运行,保证完成顺序。任务之间必须有一些同步,尤其是当您一遍又一遍地运行相同的任务时。你有一个状态机,你必须定义规则才能得到准确的结果。
-
@jdweng 我发布了一个完全异步运行的答案,并保证在您发布该评论前 12 分钟需要订购。除了 TPL 提供的同步之外,不需要同步以确保在作为其延续的任务完成后运行延续。
标签: c# asynchronous task-parallel-library task