我有两个 .net Task 对象,我可能希望它们并行或按顺序运行。无论哪种情况,我都不想阻止线程等待它们。事实证明,响应式扩展使平行故事变得非常漂亮。但是当我尝试按顺序排列任务时,代码可以工作,但感觉很尴尬。
我想知道是否有人可以展示如何使顺序版本更简洁或像并行版本一样轻松编码。没有必要使用响应式扩展来回答这个问题。
作为参考,这是我的两个并行和顺序处理解决方案。
并行处理版本
这是纯粹的快乐:
public Task<string> DoWorkInParallel()
{
var result = new TaskCompletionSource<string>();
Task<int> AlphaTask = Task.Factory.StartNew(() => 4);
Task<bool> BravoTask = Task.Factory.StartNew(() => true);
//Prepare for Rx, and set filters to allow 'Zip' to terminate early
//in some cases.
IObservable<int> AsyncAlpha = AlphaTask.ToObservable().TakeWhile(x => x != 5);
IObservable<bool> AsyncBravo = BravoTask.ToObservable().TakeWhile(y => y);
Observable
.Zip(
AsyncAlpha,
AsyncBravo,
(x, y) => y.ToString() + x.ToString())
.Timeout(TimeSpan.FromMilliseconds(200)).Subscribe(
(x) => { result.TrySetResult(x); },
(x) => { result.TrySetException(x.GetBaseException()); },
() => { result.TrySetResult("Nothing"); });
return result.Task;
}
顺序/流水线处理版本
这可行,但很笨拙:
public Task<string> DoWorkInSequence()
{
var result = new TaskCompletionSource<string>();
Task<int> AlphaTask = Task.Factory.StartNew(() => 4);
AlphaTask.ContinueWith(x =>
{
if (x.IsFaulted)
{
result.TrySetException(x.Exception.GetBaseException());
}
else
{
if (x.Result != 5)
{
Task<bool> BravoTask = Task.Factory.StartNew(() => true);
BravoTask.ContinueWith(y =>
{
if (y.IsFaulted)
{
result.TrySetException(y.Exception.GetBaseException());
}
else
{
if (y.Result)
{
result.TrySetResult(x.Result.ToString() + y.Result.ToString());
}
else
{
result.TrySetResult("Nothing");
}
}
});
}
else
{
result.TrySetResult("Nothing");
}
}
}
);
return result.Task;
}
在上面的顺序代码中,它变得一团糟,我什至没有添加超时能力来匹配并行版本!
要求(8/6 更新)
对于那些回答,请注意:
顺序方案应该允许第一个任务的输出馈送第二个任务的输入的安排。我上面的示例“尴尬”代码很容易被安排来实现这一点。
我对 .net 4.5 答案感兴趣 - 但 .net 4.0 答案对我来说同样重要或更重要。
任务“Alpha”和“Bravo”的总时限为 200 毫秒;他们每个人都没有 200 毫秒。在顺序情况下也是如此。
如果任一任务返回无效结果,SourceCompletionTask 必须在两个任务完成之前提前完成。如示例代码中的显式测试所示,无效结果是 [AlphaTask:5] 或 [BravoTask:false]。
8/8 更新:澄清- 在顺序情况下,如果 AlphaTask 不成功或超时已经发生,则 BravoTask 根本不应该执行。假设 AlphaTask 和 BravoTask 都不能阻塞。这并不重要,但在我的实际场景中,它们实际上是异步 WCF 服务调用。
也许我可以利用 Rx 的某个方面来清理顺序版本。但即使只是任务编程本身也应该有一个更好的故事,我想。走着瞧。
勘误表在两个代码示例中,我将返回类型更改为 Task,因为海报的答案非常正确,我不应该返回 TaskCompletionSource。