Подтвердить что ты не робот

Является ли ConcurrentDictionary ключами или значениями свойство threadafe

Возьмите вопрос о безопасности потоков с помощью ConcurrentDictionary. Из API я вижу, что перечислитель является потокобезопасным, но я не вижу то же самое для свойств ключей и значений. Мой вопрос:

Безопасно ли циклически перебирать коллекцию Keys или Values, когда есть другие потоки, изменяющие ее одновременно?

4b9b3361

Ответ 1

Пока мне нравится документация, я стараюсь проверять вещи с помощью небольшой программы, когда сомневаюсь, или я чувствую, что могу предположить слишком много.

Следующий код проверяет, действительно ли вы можете перечислить коллекцию значений безопасно, добавляя или удаляя ключи из отдельного потока в тот, на котором происходит перечисление. Это не приведет к тому, что в обычной коллекции были изменены исключения. Более подробно, вот несколько тестовых примеров

Случай 1: перечисление значений и удаление ключа

Если вы выполните следующую последовательность:

  • начало перечисления коллекции значений из потока
  • удалите ключ из другого потока, который мы еще не перечисляем
  • Продолжить перечисление в исходном потоке

Наблюдаемое поведение заключается в том, что удаленный ключ действительно будет перечислить, поскольку он существовал в коллекции значений, когда мы начали перечисление. Никаких исключений не будет.

Случай 2: перечисление значений и добавление ключа

  • начало перечисления коллекции значений из потока
  • добавить новый ключ из другого потока, который мы еще не перечисляем
  • Продолжить перечисление в исходном потоке

Наблюдаемое поведение заключается в том, что добавленный ключ не будет перечисляться, поскольку он не существовал в коллекции значений, когда мы начали перечислять его. Никакое исключение не будет вызвано тем, используем ли мы TryAdd или добавляем, назначая непосредственно словарь, то есть словарь [ключ] = значение.

Пример кода

Вот примерная программа, которая демонстрирует оба случая:

ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>();

// Seed the dictionary with some arbitrary values; 
for (int i = 0; i < 30; i++)
{
    dictionary.TryAdd(i, i);
}

// Reader thread - Enumerate the Values collection
Task.Factory.StartNew(
        () =>
        {
            foreach (var item in dictionary.Values)
            {
                Console.WriteLine("Item {0}: count: {1}", item, dictionary.Count);
                Thread.Sleep(20);
            }

        }
);

// writer thread - Modify dictionary by adding new items and removing existing ones from the end
Task.Factory.StartNew(
        () =>
        {
            for (int i = 29; i >= 0; i--)
            {
                Thread.Sleep(10);
                //Remove an existing entry 
                int removedValue;
                if (dictionary.TryRemove(i, out removedValue))
                    Console.WriteLine("Removed item {0}", removedValue);
                else
                    Console.WriteLine("Did not remove item {0}", i);

                int iVal = 50 + i*2;
                dictionary[iVal] = iVal;
                Thread.Sleep(10);
                iVal++;
                dictionary.TryAdd(iVal, iVal);
            }
        }
);

Console.ReadKey();

И вот вывод в режиме выпуска:

Console output

Ответ 2

ConcurrentDictionary Представляет потокобезопасную коллекцию значений ключа пар, к которым можно одновременно обращаться несколькими потоками.

Источник: MSDN