Непонятная причина ошибки "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 шт):

Автор решения: aepot

.ToList() когда делает снапшот коллекции, так же её итерирует как и foreach. То есть исключение возникает не в цикле, а внутри ToList.

Используйте потокобезопасные коллекции или примитивы синхронизации.


Ещё посоветую делать TaArray вместо ToList, если полученную коллекцию не требуется изменять. Работа с массивами имеет выше производительность, чем со списками.

→ Ссылка