[英]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.