Task.WhenAll создает дубликаты для задачи<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 задач дали в 10 раз больше результатов, чем должны были?
- Почему, когда я заменяю ConcurrentDictionary на Dictionary, этот код работает должным образом?
1 ответ
Каждый Task
возвращается весь ConcurrentDictionary
поэтому, когда вы получите набор результатов из Task.WhenAll
, он содержит один и тот же словарь 10 раз.
Некоторые дополнительные заметки:
while (done == false);
это ужасно Это, вероятно, привязывает ваш процессор на 100%, пока он ждет. Если вы конвертируете асинхронность на основе событий в асинхронность на основе задач, конвертируйте ваши события в задачи или используйте TaskCompletionSource
Если вы можете рефакторинг так, чтобы асинхронные методы просто возвращали значения, как ValueTuple
s, Tuple
s, KeyValuePair
s, анонимные типы или ваши собственные типы, и не изменяйте словарь во время их работы, вы также можете отказаться от ConcurrentDictionary
и просто создать словарь из набора результатов с ToDictionary
после Task.WhenAll
,