Очень верно @Evk.

абочее предположение заключается в том, что LINQ является потокобезопасным при использовании сSystem.Collections.Concurrent коллекции (в том числеConcurrentDictionary).

(Другие сообщения переполнения, кажется, согласны:ссылка)

Тем не менее, проверка реализации LINQСортировать по метод расширения показывает, что он не является потокобезопасным с подмножеством параллельных коллекций, которые реализуютICollection (например.ConcurrentDictionary).

OrderedEnumerable GetEnumerator (источник здесь) создает экземплярбуфер структура (источник здесь) который пытается привести коллекцию кICollection (которыйConcurrentDictionary реализует), а затем выполняет collection.CopyTo с массивом, инициализированным по размеру коллекции.

Следовательно, еслиConcurrentDictionary (как бетонICollection в этом случае) увеличивается в размерах во время операции OrderBy, между инициализацией массива и копированием в него эта операция будет выброшена.

Следующий тестовый код показывает это исключение:

(Примечание: я ценю, что выполнениеСортировать по на поточно-безопасной коллекции, которая меняется под тобой, не так уж и многозначительно, но я не верю, что ее следует бросать)

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                int loop = 0;
                while (true) //Run many loops until exception thrown
                {
                    Console.WriteLine($"Loop: {++loop}");

                    _DoConcurrentDictionaryWork().Wait();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        private static async Task _DoConcurrentDictionaryWork()
        {
            var concurrentDictionary = new ConcurrentDictionary<int, object>();
            var keyGenerator = new Random();
            var tokenSource = new CancellationTokenSource();

            var orderByTaskLoop = Task.Run(() =>
            {
                var token = tokenSource.Token;
                while (token.IsCancellationRequested == false)
                {
                    //Keep ordering concurrent dictionary on a loop
                    var orderedPairs = concurrentDictionary.OrderBy(x => x.Key).ToArray(); //THROWS EXCEPTION HERE

                    //...do some more work with ordered snapshot...
                }
            });

            var updateDictTaskLoop = Task.Run(() =>
            {
                var token = tokenSource.Token;
                while (token.IsCancellationRequested == false)
                {
                    //keep mutating dictionary on a loop
                    var key = keyGenerator.Next(0, 1000);
                    concurrentDictionary[key] = new object();
                }
            });

            //Wait for 1 second
            await Task.Delay(TimeSpan.FromSeconds(1));

            //Cancel and dispose token
            tokenSource.Cancel();
            tokenSource.Dispose();

            //Wait for orderBy and update loops to finish (now token cancelled)
            await Task.WhenAll(orderByTaskLoop, updateDictTaskLoop);
        }
    }
}

ЧтоСортировать по Бросок исключения приводит к одному из нескольких возможных выводов:

1) Мое предположение о том, что LINQ является потокобезопасным для одновременных коллекций, неверно, и безопасно выполнять LINQ только для коллекций (будь они одновременными или нет), которые не изменяются во время запроса LINQ.

2) Существует ошибка с реализацией LINQСортировать по и для реализации некорректно пытаться преобразовать исходную коллекцию в коллекцию ICollection и попытаться выполнить копию коллекции (и она должна просто перейти к своему поведению по умолчанию, повторяя IEnumerable).

3) Я неправильно понял, что здесь происходит ...

Мысли высоко ценится!

Ответы на вопрос(0)

Ваш ответ на вопрос