#c# #multithreading #dictionary #concurrency #thread-safety
#c# #многопоточность #словарь #параллелизм
Вопрос:
У меня проблема с добавлением нового элемента в статический словарь при использовании его из нескольких потоков. Есть идеи, где я делаю это неправильно? Инициализация словаря:
public static class Server
{
public static volatile Dictionary<int, List<SomeClass>> Values;
}
Попытка добавить элемент:
Server.Values.Add(someInt, new List<SomeClass> { elements});
Комментарии:
1. Да, вы используете тип, который задокументирован как не являющийся потокобезопасным, из нескольких потоков без каких-либо средств обеспечения безопасности.
2. Думаю, я мог бы догадаться, но почему бы вам не рассказать нам, в чем проблема, а не заставлять нас гадать?
3. в чем ошибка?
4. вот в чем проблема — studio не выдает ошибку — она просто ломается
5. Определение «разрывов»
Ответ №1:
Как объяснил Джон Скит, вы используете объект, который не гарантированно является потокобезопасным
попробуйте использовать ConcurrentDictionary
который предназначен для сценария параллелизма со многими потоками
public static class Server
{
public static ConcurrentDictionary<int, List<SomeClass>> Values =
new ConcurrentDictionary<int, List<SomeClass>>();
}
Вот как это использовать
bool added = Server.Values.TryAdd(someInt, new List<SomeClass> { elements});
Комментарии:
1. @TimSchmelter Молодец, что упомянул об этом, я забыл его добавить. Это было ограничение по времени
2. в этом случае added = false и Server.Values = null . Это самая большая «ошибка», которую я получаю
3. сначала вы должны его инициализировать, я обновил свой ответ
Ответ №2:
В общем, при работе с ресурсами, которые совместно используются несколькими потоками, вам необходимо использовать механизм синхронизации, например lock()
, для обеспечения потокобезопасности вашего кода. Создайте общий объект для использования в качестве блокировки:
private object _lock = new object();
Затем вы окружаете любой код, который обращается к вашему общему ресурсу, следующим образом:
lock(_lock)
{
// perform operations on shared resource here.
}
Важно отметить, что у вас должна быть отдельная блокировка для каждого общего ресурса, а не одна блокировка, используемая для всех ресурсов. Если вы используете свой объект блокировки с несколькими ресурсами, ваш код может быть очень неэффективным. Если один поток захватывает блокировку, чтобы он мог использовать ресурс A, тогда другим потокам придется ждать снятия блокировки, даже если они хотят получить доступ к ресурсу B, который не имеет ничего общего с ресурсом A. Поэтому лучше иметь один объект блокировки на ресурс и называть свои объекты блокировки, чтобы вы знали, с какими ресурсами они должны использоваться.
Альтернативой этому (как показывает ответ БРАХИМА Камеля) является использование замены, если таковая имеется, для вашего общего ресурса, в который уже встроена синхронизация потоков, например ConcurrentDictionary
. Хотя в вашем случае это может быть невозможно.
Комментарии:
1. зачем заново вводить weel, если у вас есть ConcurrentDictionary: p
2. это стоит упомянуть на случай, если он не может изменить тип 1
3. Я бы предпочел переменную
ServerDictionaryLock
, тогда будет ясно, что вы блокируете с ее помощью.4. @TimSchmelter — ну, я попытался сделать его как можно более общим, но я понимаю вашу точку зрения.
5. @rory.ap: проблема с
_lock
объектом заключается в том, что люди реализуют его таким образом, и он предлагает использовать одну переменную блокировки для каждого класса. Это заблокирует весь экземпляр и любое действие, которое его использует.