Task.WhenAll 为 Task <ConcurrentDictionary>创建重复项



创建任务列表的类,每个任务返回ConcurrentDictionary

public List<Task<ConcurrentDictionary<int, object>>> GetDictionaries()
{
var results = new ConcurrentDictionary<int, object>();
var tasks = new List<Task<ConcurrentDictionary<int, object>>>();
for (var k = 0; k < 10; k++)
{
tasks.Add(Task.Run(() =>
{
var done = false;
var data = new Object();
var eventCallback = (int id) => { data.id = id; done = true; };
Client.asyncEvent += eventCallback;
Client.initiateAsyncEvent(k);
while (done == false);
Client.asyncEvent -= eventCallback;
results[k] = data; 
return results;
}));
}
return tasks;
}

调用事件(任务(10次,等待此事件的回调,将结果添加到字典"results"中。

我们执行 10 个事件(任务(,所以应该在字典中获取 10 个项目,但是当我将所有任务的字典与 When.All 合并时,列表包含 100 个项目而不是 10 个项目。

var tasks = GetDictionaries();
var plainListOfResults = Task
.WhenAll(tasks)
.Result
.SelectMany(o => o.Keys)
.ToList();
// Expected: [0,1,2,3,4,5,6,7,8,9]
// Actual: [0,1,2,3,4,5,6,7,8,9, 0,1,2,3,4,5,6,7,8,9 ... 0,1,2,3,4,5,6,7,8,9]

问题

为什么 10
  1. 个任务创建的结果是应有的结果的 10 倍?
  2. 为什么当我用字典替换ConcurrentDictionary时,这段代码可以按预期工作?

每个Task都返回整个ConcurrentDictionary,因此当您从Task.WhenAll获取结果集时,它包含相同的字典10次。

一些附加说明:

while (done == false);太可怕了。在等待时,它可能将您的 CPU 固定在 100%。如果要将基于事件的异步转换为基于任务的异步,请将事件转换为任务,或使用TaskCompletionSource

https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types

如果可以重构以使异步方法仅返回值,例如ValueTuples、Tuples、KeyValuePairs、匿名类型或您自己的类型,并且在它们运行时不修改字典,您也可以放弃ConcurrentDictionary,只从结果集创建字典,并在Task.WhenAll之后使用ToDictionary创建字典。

相关内容

  • 没有找到相关文章

最新更新