Task.WhenAll создает дубликаты для Task <ConcurrentDictionary> - PullRequest
0 голосов
/ 01 июля 2018

Класс, который создает список задач, каждая задача возвращает 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. Почему, когда я заменяю ConcurrentDictionary на Словарь, этот код работает должным образом?

1 Ответ

0 голосов
/ 01 июля 2018

Каждое Task возвращает все ConcurrentDictionary, поэтому, когда вы получаете набор результатов из Task.WhenAll, он содержит один и тот же словарь 10 раз.

Некоторые дополнительные примечания:

while (done == false); ужасно. Это, вероятно, привязывает ваш процессор на 100%, пока он ждет. Если вы конвертируете асинхронность на основе событий в асинхронность на основе задач, конвертируйте ваши события в задачи или используйте TaskCompletionSource

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

Если вы можете выполнить рефакторинг, чтобы асинхронные методы просто возвращали значения, такие как ValueTuple s, Tuple s, KeyValuePair s, анонимные типы или ваши собственные типы, и не изменяли словарь во время работы, Вы также можете отказаться от ConcurrentDictionary и просто создать словарь из набора результатов с помощью ToDictionary после Task.WhenAll.

...