如果我的代码通过返回表示每个阶段的Task来抽象分阶段的异步操作序列,那么如何确保延续按阶段顺序执行(即任务完成的顺序)?
请注意,这与“不浪费时间等待较慢的任务”不同。在调度中没有竞争条件的情况下需要保证订单。这个更宽松的要求可以通过以下问题的部分答案来解决:
我认为逻辑解决方案是使用自定义TaskScheduler(例如基于SynchronizationContext的一个)附加延续。但是,我无法保证在任务完成时同步执行 schedule 延续。
在代码中,这可能类似于
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
显然,如果前一阶段失败,该示例缺少将异常转发(或取消)后续阶段的详细信息,但这只是一个例子。
答案 0 :(得分:3)
每个阶段只需await
,然后再进入下一个......
public static async Task ProcessStagedOperation(StagedOperation operation, int i)
{
await operation.Connect;
Console.WriteLine("{0}: Connected", i);
await operation.Accept;
Console.WriteLine("{0}: Accepted", i);
await operation.Complete;
Console.WriteLine("{0}: Completed", i);
}
然后,您可以在for
循环中调用该方法。
答案 1 :(得分:2)
如果使用TAP(任务异步编程),即async
和await
,则可以使处理流程更加明显。在这种情况下,我将创建一个新方法来封装操作顺序:
public async Task ProcessStagedOperation(StagedOperation op, int i)
{
await op.Connect;
Console.WriteLine("{0}: Connected", i);
await op.Accept;
Console.WriteLine("{0}: Accepted", i)
await op.Complete;
Console.WriteLine("{0}: Completed", i)
}
现在你的处理循环有点简化了:
public async Task RunOperations()
{
List<Task> pendingOperations = new List<Task>();
for (int i=0; i<3; i++)
{
var op = InitiateStagedOperation(i);
pendingOperations.Add(ProcessStagedOperation(op, i));
}
await Task.WhenAll(pendingOperations); // finish
}
现在,您可以引用可以明确等待的任务对象,或者只是从另一个上下文中await
。 (或者你可以简单地忽略它)。我修改RunOperations()
方法的方法允许您创建一个大型待处理任务队列,但在等待它们全部完成时不会阻塞。