我经常在代码中拥有一组独立任务,这些任务创建了可以立即启动的其他独立任务的集合,因此从时间上看,如下所示。
A0 -------
B0 --------- C0 --------- ...
B1 ------ C1 ------------ ...
B2------------------- C2 ...
A1 ---
B3 -------- C3--------------- ...
B4 -- C4 ------------------- ...
我通常要做的是像这样的嵌套方法
.
.
.
await Task.WhenAll(A0, A1)
.
.
.
private static async ExecuteA(Task A)
{
var results = await A;
var BsS = results.Select(r => ExecuteBC(r));
await Task.WhenAll(BCs);
private static async ExecuteBC(string value)
{
var result = await ExecuteB(value);
await ExecuteC(result);
}
是否有更优雅的方法来链接嵌套的异步任务?
答案 0 :(得分:2)
如果Task A
集Task Bs
完成后不需要处理任何内容,则可以返回IEnumerable<Task>
,然后等待所有这些。
await Task.WhenAll(ExecuteA(A0).Concat(ExecuteA(A1)))
...
private static async Task<IEnumerable<Task>> ExecuteA(Task task)
{
var results = await task;
return results.Select(r => ExecuteB(r));
}
private static async Task ExecuteB(string value)
{
...
}
如果您需要在ExecuteA内部处理ExecuteB的结果,那么我会对您编写的代码感到满意。
顺便说一句,我发现将Task
s传递给方法并不常见,并且通常在方法调用之外等待。
答案 1 :(得分:2)
我拿了您的基本示例代码并将其充实,直到产生结果:
async Task Main()
{
string[] a0_source = new[] { "Hello", "World" };
string[] a1_source = new[] { "Hi", "There" };
Task<string[]> A0 = ExecuteA(a0_source);
Task<string[]> A1 = ExecuteA(a1_source);
var results = await Task.WhenAll(A0, A1);
var output = String.Join(", ", results.SelectMany(x => x));
Console.WriteLine(output);
}
private static async Task<string[]> ExecuteA(string[] A)
{
var BCs = A.Select(r => ExecuteBC(r));
return await Task.WhenAll(BCs);
}
private static async Task<string> ExecuteBC(string value)
{
var result = await ExecuteB(value);
return await ExecuteC(result);
}
private static async Task<string> ExecuteC(string value)
{
return await Task.Run(() => value + "!C");
}
private static async Task<string> ExecuteB(string value)
{
return await Task.Run(() => value + "!B");
}
这会在控制台上产生Hello!B!C, World!B!C, Hi!B!C, There!B!C
。
然后我介绍了Microsoft的Reactive Framework(NuGet“ System.Reactive”),并产生了以下中间结果:
async Task Main()
{
string[] a0_source = new[] { "Hello", "World" };
string[] a1_source = new[] { "Hi", "There" };
IObservable<string> query =
from a in a0_source.Concat(a1_source).ToObservable()
from b in Observable.FromAsync(() => ExecuteB(a))
from c in Observable.FromAsync(() => ExecuteC(b))
select c;
var output = String.Join(", ", await query.ToArray());
Console.WriteLine(output);
}
private static async Task<string> ExecuteC(string value)
{
return await Task.Run(() => value + "!C");
}
private static async Task<string> ExecuteB(string value)
{
return await Task.Run(() => value + "!B");
}
然后您可以将其更进一步并执行以下操作:
async Task Main()
{
string[] a0_source = new[] { "Hello", "World" };
string[] a1_source = new[] { "Hi", "There" };
IObservable<string> query =
from a in a0_source.Concat(a1_source).ToObservable()
from b in Observable.Start(() => a + "!B")
from c in Observable.Start(() => b + "!C")
select c;
var output = String.Join(", ", await query.ToArray());
Console.WriteLine(output);
}
现在看来还算优雅。最好的一点是,您可以通过简单的.ToTask()
调用轻松地将可观察对象转换为任务。还可以观察到实物。这是一个容易更换的替代品,更加优雅。最好的事情是它也使用LINQ语法。让我知道是否需要更多说明。