[英]How to pass additional context information along with tasks to Task.WhenAll?
考虑以下理想代码(不起作用)。 我基本上是在尝试创建一个返回特定对象的Task
列表,将它们与string
标识符相关联,然后使用Task.WhenAll
批量执行所有这些。 在执行结束时,我需要让这些Task
的结果仍然与它们最初创建时使用的字符串标识符相关联:
public async Task<SomeObject> DoSomethingAsync(string thing)
{
// implementation elided
}
public async Task<SomeObject> DoSomethingElseAsync(string thing)
{
// different implementation elided
}
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
var tasks = new List<(string, Task<SomeObject>)>();
foreach (var item in someListOfStrings)
{
if (condition)
{
tasks.Add((item, DoSomethingAsync(item)));
}
else
{
tasks.Add((item, DoSomethingElseAsync(item)));
}
}
// this doesn't compile, i'm just demonstrating what i want to achieve
var results = await Task.WhenAll(tasks);
return results;
}
这可以重写为以下内容:
public async Task<(string, SomeObject)> DoSomethingWrapperAsync(string thing)
=> (thing, await DoSomethingAsync(thing));
public async Task<(string, SomeObject)> DoSomethingElseWrapperAsync(string thing)
=> (thing, await DoSomethingElseAsync(thing));
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
var tasks = new List<Task<(string, SomeObject)>>();
foreach (var thing in someListOfStrings)
{
if (condition)
{
tasks.Add(DoSomethingWrapperAsync(thing));
}
else
{
tasks.Add(DoSomethingElseWrapperAsync(thing));
}
}
// this does compile
var results = await Task.WhenAll(tasks);
return results;
}
问题是我需要一个额外的包装器方法来处理我将要调用的每个可能的离散异步函数,这感觉不必要和浪费并且代码很多(因为会有很多这些方法)。 有没有更简单的方法来实现我的需要?
我研究了实现 awaitable/awaiter 模式,但看不到如何让它与Task.WhenAll
一起工作,它需要Task
或Task<TResult>
的集合,因为指导似乎是“不要扩展那些类” 。
您可以随时进行压缩:
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
var tasks = someListOfStrings
.Select(async item =>
condition ?
(item, await DoSomethingAsync(item)) :
(item, await DoSomethingElseAsync(item)))
.ToList();
return await Task.WhenAll(tasks);
}
或者,您可以将输入作为单独的集合保存,稍后再压缩:
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
// Reify input so we don't enumerate twice.
var input = someListOfStrings.ToList();
var tasks = input
.Select(item =>
condition ?
DoSomethingAsync(item) :
DoSomethingElseAsync(item))
.ToList();
var taskResults = await Task.WhenAll(tasks);
return input.Zip(taskResults, (item, taskResult) => ((item, taskResult)));
}
据我所知,您正在使用condition
来确定正在调用哪个方法(具有完全相同的签名)。 为什么不传递为每个项目调用的回调,而不是在foreach
循环中执行逻辑?
public async Task<SomeObject> DoSomethingAsync(string thing)
{
// ...
}
public async Task<SomeObject> DoSomethingElseAsync(string thing)
{
// ...
}
public async Task<IEnumerable<(string, SomeObject)>> DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
Func<string, Task<SomeObject>> callback = condition ? DoSomethingAsync : DoSomethingElseAsync;
var results = await Task.WhenAll(
someListOfStrings.Select(async thing => (thing, await callback(thing)))
);
return results;
}
此外,您可以将其提取为扩展方法。
public static class AsyncExtensions
{
public static async Task<IEnumerable<(T, TResult)>> WhenAllAsync(this IEnumerable<T> collection, Func<T, Task<TResult>> selector)
{
var results = await Task.WhenAll(
collection.Select(async item => (item, await selector(item)))
);
return results;
}
}
public async Task MyMethodAsync()
{
// ...
var results = await myListOfStrings.WhenAllAsync(condition ? DoSomethingAsync : DoSomethingElseAsync);
// ...
}
或者在回调中做映射。
public static class AsyncExtensions
{
public static Task<IEnumerable<TResult>> WhenAllAsync(this IEnumerable<T> collection, Func<T, Task<TResult>> selector)
=> Task.WhenAll(collection.Select(selector)));
}
public async Task MyMethodAsync()
{
// ...
var results = await myListOfStrings.WhenAllAsync(async thing => (thing, await (condition ? DoSomethingAsync(thing) : DoSomethingElseAsync(thing))));
// ...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.