最初,我有以下内容:
foreach (Product product in products)
{
product.ImageUri = _imageClient.GetImageUri(product.GetImagePath());
}
我想做的是并行处理所有产品,而不是一次处理一个。我已经将_imageClient.GetImageUri(...)更新为_imageClient.GetImageUriAsync(...)。我现在可以执行以下操作:
List<Task<Uri>> tasks = new List<Task<Uri>>();
foreach (Product product in products)
{
Task<Uri> task = _imageClient.GetImageUriAsync(product.GetImagePath());
tasks.Add(task);
}
var results = await Task.WhenAll(tasks);
这种方法的问题在于,我现在必须遍历结果,将每个结果与正确的Product匹配并分配属性。
是否有一种将两种方法结合在一起的方法,以便我可以将属性分配作为任务的一部分,以便所有这些都可以并行运行?
答案 0 :(得分:2)
那又怎么样:
List<Task<Uri>> tasks = new List<Task<Uri>>();
foreach (Product product in products)
{
tasks.Add(Task.Run( async () => product.ImageUri = await _imageClient.GetImageUriAsync(product.GetImagePath()) ));
}
await Task.WhenAll(tasks);
您甚至根本不需要真正的结果。
答案 1 :(得分:1)
您也可以在单个select语句中启动它们,并在同一行中等待它们:
await Task.WhenAll(products.Select(async p => p.ImageUri = await _imageClient.GetImageUriAsync(p.GetImagePath())));
这将并行执行所有任务并初始化属性。 Task.WhenAll然后将等待它们中最慢的一个。
这是一个LINQPad示例,以演示其工作原理:
void Main()
{
Do();
}
public async Task Do()
{
List<Product> products = new List<UserQuery.Product>
{
new Product(),
new Product(),
new Product(),
new Product(),
};
Stopwatch sw = new Stopwatch();
Console.WriteLine("START");
sw.Start();
await Task.WhenAll(products.Select(async x => x.MyProperty = await GetNumber()));
sw.Stop();
Console.WriteLine($"ENDE: {sw.ElapsedMilliseconds}");
products.Dump();
}
public async Task<int> GetNumber()
{
Random rand = new Random(DateTime.Now.Millisecond);
return await Task.Run(() => { System.Threading.Thread.Sleep(4000); return rand.Next(1,1000);});
}
class Product
{
public int MyProperty { get; set; }
}
输出: