У меня есть ConcurrentDictionary
stoing Item
s:
ConcurrentDictionary<ulong, Item> items;
Теперь я хотел бы заблокировать Item
из этого словаря, чтобы я мог безопасно работать с ним.
Правильно ли этот код?
try
{
Item item;
lock(item = items[itemId])
{
// do stuff
// possibly remove itemId from the dictionary
}
}
catch(KeyNotFoundException)
{
// ...
}
Боюсь, что это так.Я полагаю, lock(item = items[itemId])
можно разложить на две операции:
- Назначить ссылку
items[itemId]
на item
- Блокировка на
item
Не обязательно атомарные.
Итак, я боюсь следующего состояния гонки:
- Поток 1 выполняет
item = items[itemId]
, но еще не выполняет lock(item)
- Поток 2 выполняет
lock(item = items[itemId])
для того же значения itemId
- Поток 2 стирает
itemId
с items
- Поток 2 снимает свою блокировку
- Поток 1 выполняет
lock(item)
, не зная, что itemId
больше не находится в словаре - Поток 1 неправильно работает с
item
, вместо того, чтобы идти к его блоку catch
, как и должно быть.
Является ли приведенный выше анализ правильным?
В таком случае, будет ли достаточно изменить мой код таким образом?
try
{
Item item;
lock(items[itemId])
{
item = items[itemId];
// do stuff
// possibly remove itemId from the dictionary
}
}
catch(KeyNotFoundException)
{
// ...
}
РЕДАКТИРОВАТЬ: потому что яначинаю предполагать, что я влюбился в проблему XY.Вот фон.
Многопользовательская игра в шахматы.itemId
- это идентификатор игры.item
- текущее состояние игры.Дикт содержит текущие предметы.Операция состоит в том, чтобы обработать ход игрока, как «рыцарь с e3 идет на d1».Если из-за хода игрока игра заканчивается, то мы возвращаем конечное состояние игры и удаляем игру из словаря.Разумеется, недопустимо совершать любые дальнейшие ходы в завершенной игре, отсюда и блок try / catch.
Блок try / catch должен правильно определять следующие ситуации:
- игрок посылает недопустимую команду, приказывая сделать ход в несуществующей игре;
- Из-за задержки в сети, команда игроков, чтобы сделать ход в действительной игре, поступает на сервер после того, кактаймер истек.