创建任务列表的类,每个任务返回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- 个任务创建的结果是应有的结果的 10 倍?
- 为什么当我用字典替换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
如果可以重构以使异步方法仅返回值,例如ValueTuple
s、Tuple
s、KeyValuePair
s、匿名类型或您自己的类型,并且在它们运行时不修改字典,您也可以放弃ConcurrentDictionary
,只从结果集创建字典,并在Task.WhenAll
之后使用ToDictionary
创建字典。