Ответ 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();
И вот вывод в режиме выпуска: