我正在阅读 this post关于Parallel.ForEach
其中指出“Parallel.ForEach 与传入异步方法不兼容。”
所以,为了检查我写了这段代码:
static async Task Main(string[] args)
{
var results = new ConcurrentDictionary<string, int>();
Parallel.ForEach(Enumerable.Range(0, 100), async index =>
{
var res = await DoAsyncJob(index);
results.TryAdd(index.ToString(), res);
});
Console.ReadLine();
}
static async Task<int> DoAsyncJob(int i)
{
Thread.Sleep(100);
return await Task.FromResult(i * 10);
}
此代码填写results
字典并发。
顺便说一下,我创建了一个 ConcurrentDictionary<string, int>
类型的字典因为万一我有 ConcurrentDictionary<int, int>
当我在 Debug模式下探索它的元素时,我看到元素是按键排序的,我认为相应地添加了元素。
那么,我想知道我的代码是否有效?如果它“与传入异步方法不兼容”,为什么它运行良好?
此代码之所以有效,是因为 DoAsyncJob
并不是真正的异步方法。 async
不会使方法异步工作。等待由 Task.FromResult
返回的已完成任务也是同步的。 async Task Main
不包含任何导致编译器警告的异步代码。
演示如何 Parallel.ForEach
的示例不适用于异步方法应该调用真正的异步方法:
static async Task Main(string[] args)
{
var results = new ConcurrentDictionary<string, int>();
Parallel.ForEach(Enumerable.Range(0, 100), async index =>
{
var res = await DoAsyncJob(index);
results.TryAdd(index.ToString(), res);
});
Console.WriteLine($"Items in dictionary {results.Count}");
}
static async Task<int> DoAsyncJob(int i)
{
await Task.Delay(100);
return i * 10;
}
结果会是
Items in dictionary 0
Parallel.ForEach没有接受 Func<Task>
的过载, 它只接受 Action
代表们。这意味着它不能等待任何异步操作。
async index
被接受是因为它隐式地是一个 async void
delegate .至于Parallel.ForEach
而言,它只是一个Action<int>
.
结果是 Parallel.ForEach
触发 100 个任务,从不等待它们完成。这就是当应用程序终止时字典仍然为空的原因。