1

创建任务列表的类,每个任务返回 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 次,等待此事件的回调,将结果添加到字典“结果”中。

我们执行 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]

问题

  1. 为什么 10 项任务创造的结果是应有的 10 倍?
  2. 为什么,当我用 Dictionary 替换 ConcurrentDictionary 时,此代码按预期工作?
4

1 回答 1

3

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

一些附加说明:

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

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

如果您可以重构以便异步方法只返回值,如ValueTuples、Tuples、KeyValuePairs、匿名类型或您自己的类型,并且在运行时不修改字典,您也可以放弃ConcurrentDictionary并只从ToDictionary之后的一组结果Task.WhenAll

于 2018-07-01T08:46:05.907 回答