Непонятная причина ошибки "Collection was modified; enumeration operation may not execute"
У меня есть механизм типа pub/sub
, реализованный через Queue<T>
, где я в delegate
помещаю сообщение в очередь, а затем в общем цикле начинаю их обработку используя Dataflow.ActionBlock
для того чтобы ограничить максимальное количество блоков через MaxDegreeOfParallelism
:
Console.WriteLine($"Processing {ClosedOrders.Count} closed orders ...");
var closedOrdersBlock = new ActionBlock<OrderMessage>(OnPositionClosed,
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 8 });
while (ClosedOrders.Count > 0)
{
ClosedOrders.TryDequeue(out var closedOrder);
if (closedOrder != null)
closedOrdersBlock.Post(closedOrder);
}
closedOrdersBlock.Complete();
closedOrdersBlock.Completion.Wait();
Вот обработка delegate:
protected virtual void OnPositionClose(OrderMessage message)
{
try
{
foreach (var orderToClose in ClosedOrders.ToList())
{
if (message.Order == null || orderToClose.Order == null ||
orderToClose.Order.Ticket != message.Order.Ticket) continue;
Console.WriteLine("Duplicate order for closing received for ticket " + orderToClose.Order.Ticket +
", skipping");
return;
}
ClosedOrders.Enqueue(message);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to enqueue order with ticket {message.Order?.Ticket} for closing: {ex.Message}");
Console.WriteLine($"Stack trace: {ex.StackTrace}");
}
}
Возникает ошибка
"Failed to enqueue order for closing: Collection was modified; enumeration operation may not execute."
, которая как я понимаю возникает из-за того что я добавляю в очередь новые записи в то время пока ведется обработка очереди, хотя исключение говорит что это случается в самом делегате в цикле foreach. Что это означает? В нём я не меняю, а просто прохожусь по списку и проверяю данные, но никак его не изменяю.
Или это связано с тем что пока ведется обработка новая запись помещается в очередь?
Ответы (1 шт):
.ToList()
когда делает снапшот коллекции, так же её итерирует как и foreach
. То есть исключение возникает не в цикле, а внутри ToList
.
Используйте потокобезопасные коллекции или примитивы синхронизации.
Ещё посоветую делать TaArray
вместо ToList
, если полученную коллекцию не требуется изменять. Работа с массивами имеет выше производительность, чем со списками.