我的要求很奇怪。
我SomeMethod()
调用了GetDataFor()
。
public void SomeMethod()
{
for(int i = 0; i<100; i++) {
var data = GetDataFor(i);
}
}
public data GetDataFor(int i) {
//call a remote API
//to generate data for i
//store to database
return data;
}
对于每个i
,最终结果将始终不同。在致电GetDataFor(i)
之前,无需等待 GetDataFor(i+1)
完成。
换句话说,我需要:
GetDataFor()
后,i+1
致电i
(并行调用它们看起来不可能)GetDataFor()
的100个实例已完成运行SomeMethod()
关注YK1's answer,我试图像这样修改它:
public async Task<void> SomeMethod()
{
for(int i = 0; i < 100; i++) {
var task = Task.Run(() => GetDataFor(i));
var data = await task;
}
}
它没有抛出任何错误,但我需要理解这背后的概念:
task
如何区分await
的不同调用?它越来越多了。答案 0 :(得分:9)
您可以使用Parallel.For
:
public void SomeMethod()
{
Parallel.For(0, 100, i =>
{
var data = GetDataFor(i);
//Do something
});
}
public data GetDataFor(int i)
{
//generate data for i
return data;
}
修改强>
并行循环的语法与您已经知道的for
和foreach
循环非常相似,但并行循环在具有可用内核的计算机上运行得更快。另一个不同之处在于,与顺序循环不同,并行循环的定义不是为并行循环定义的。步骤通常同时并行进行。有时,两个步骤的顺序与循环顺序时相反。唯一的保证是所有循环的迭代都将在循环结束时运行。
对于并行循环,并行程度不需要由代码指定。相反,运行时环境在尽可能多的内核上同时执行循环的步骤。无论有多少核心,循环都能正常工作。如果只有一个核心,则性能接近(相当于几个百分点)顺序等效。如果有多个核心,性能会提高;在许多情况下,性能与核心数量成比例地提高。
您可以看到更详细的解释here。
答案 1 :(得分:2)
我会将每个任务添加到集合中,然后在循环之后等待整个集合。
在这样的循环中等待会产生大量的延续和更多的开销,包括等待每个调用完成后再继续循环我相信。
请等一下等待Task.WaitAll。
如果相反,每个任务的值对于处理很重要,那么请查看等待Task.WhenAll,然后将每个任务的结果读入您的返回集合。
答案 2 :(得分:2)
有几种不同的方法。
首先,您可以保持同步并且只是并行执行它们(在不同的线程上)。并行LINQ优于Parallel
,如果要在继续之前收集调用方法中的所有结果:
public data[] SomeMethod()
{
return Enumerable.Range(0, 100)
.AsParallel().AsOrdered()
.Select(GetDataFor).ToArray();
}
其次,你可以使它异步。要使某些东西真正异步,你需要从最低级别开始(在这种情况下,&#34;调用远程API&#34;以及&#34;存储到数据库&#34;)并首先进行异步 。然后你可以使GetDataFor
异步:
public async Task<data> GetDataForAsync(int i)
{
await .. //call a remote API asynchronously
await .. //store to database asynchronously
return data;
}
然后你也可以使SomeMethod
异步:
public Task<data[]> SomeMethodAsync()
{
return Task.WhenAll(
Enumerable.Range(0, 100).Select(GetDataForAsync)
);
}
使代码异步更有效 - 代码必须更改 - 但在可伸缩性和资源使用方面它更好。
答案 3 :(得分:2)
使用async
await
时,您基本上是在说#34;等待此任务完成时,请继续做一些不依赖于此的独立工作任务&#34 ;.由于您不在乎等待GetDataFor完成,因此您并不想使用async
await
。
This previous question似乎与您的要求非常相似。考虑到这一点,我认为你应该能够做到这样的事情:
public void SomeMethod()
{
Task.Run(() => GetDataFor(i));
}
基本上,这假设你不需要在做任何其他事情之前等待GetDataFor完成,它实际上是“开始并忘记”。
关于Parallel.For,只要你有超过1个核心,你可能会看到性能有所改善。如果没有,您可能会看到性能略有下降(更多开销)。 Here's an article有助于解释其工作原理。
<强> 更新 强>
根据您的评论,我会建议:
var TList = new List<Task>();
for (var i = 0; i < 100; i++)
{
TList.Add(Task.Run(() => GetDataFor(i)));
}
await Task.WhenAll(TList);
Here's a useful question突出显示了您可能希望使用WhenAll而不是WaitAll的原因。
您可能希望在完成任务的状态中包含一些检查,以查看哪些失败(如果有)。有关示例,请参阅here。
答案 4 :(得分:1)
代码实际上毫无意义。
任务如何区分不同的等待呼叫?它越来越好了 覆盖。
它不会被覆盖。因为...
for(int i = 0; i < 100; i++) {
var task = Task.Run(() => GetDataFor(i));
var data = await task;
}
这是在继续循环之前为每个请求完成等待。等待等待结束。
这意味着整个任务是无关紧要的 - 这里没有任何并行发生。你可以在没有任务的情况下减少一些小额开销。
我怀疑OP想要实现他根本没有做到的事情,他没有花足够的时间进行调试,以便意识到他已经单独完成了整个循环。
答案 5 :(得分:0)
虽然您的原始代码覆盖了这些值,但您似乎正在尝试合并并行操作的结果。如果是,请考虑使用Task.ContinueWith来处理返回值。您的代码看起来像这样:
public void SomeMethod()
List<Task> tasks = new List<Task>();
for (var i = 0; i < 100; i++)
{
tasks.Add(Task.Run(() => GetDataFor(i)).ContinueWith((antecedent) => {
// Process the results here.
}));
}
Task.WaitAll(tasks.ToArray());
}