以您的方法为基础并进行了一些调整:
var source = new CancellationTokenSource();
var token = source.Token;
var taskA = Task.Factory.StartNew(
() => OperationA()
);
var taskAFinished = taskA.ContinueWith(antecedent =>
{
source.Cancel();
return antecedent.Result;
});
var taskB = Task.Factory.StartNew(
() => OperationB(token), token
);
var taskBFinished = taskB.ContinueWith(antecedent =>
{
switch (antecedent.Status)
{
case TaskStatus.RanToCompletion:
case TaskStatus.Canceled:
try
{
return ant.Result;
}
catch (AggregateException ae)
{
// Operation was canceled before start if OperationA is short
return null;
}
case TaskStatus.Faulted:
return null;
}
return null;
});
做了两个延续,返回相应操作的结果,所以我可以等待它们都完成(试图只用第二个做,但没有用)。
var tasks = new Task[] { taskAFinished, taskBFinished };
Task.WaitAll(tasks);
第一个只是进一步传递前面的任务 Result,第二个在 OperationB 中获取此时可用的聚合结果(RanToCompletion 和 Canceled 状态都被认为是正确的流程结束)。OperationB 现在看起来像这样:
public static List<Result> OperationB(CancellationToken token)
{
var resultsList = new List<Result>();
while (true)
{
foreach (var op in operations)
{
resultsList.Add(op.GetResult();
}
if (token.IsCancellationRequested)
{
return resultsList;
}
}
}
稍微改变逻辑 - OperationB 中的所有循环现在都被视为单个任务,但这比保持它们的原子性并编写某种协调原语来收集每次运行的结果要容易。如果我真的不在乎哪个循环产生了哪个结果,这似乎是一个不错的解决方案。如果需要,以后可能会改进为更灵活的实现(我实际上正在寻找的是递归链接多个操作 - OperationB 本身可能具有较小的重复 OperationC 内部具有相同的行为,OperationC - 当 C 处于活动状态时运行的多个 OperationD 等)。
编辑
在 taskBfinished 中添加了异常处理,以应对 OperationA 快速且在 OperationB 启动之前发出取消的情况。