Одна проблема, которую я вижу с кодом, заключается в том, что это приведет к проблемам, особенно когда происходят исключения.
obj1->obtainMutex(); //Should I create a mutex for each object so that I could obtain mutex when I am going to update fields for obj1?
pthread_mutex_unlock(&mutexA); //release mutex for unordered_map so that other threads could access other object
obj1->field1 = 1;
performOperation(obj1);
Если performOperation выдает исключение, то obj1-> releaseMutex(); никогда не будет вызван, таким образом, оставляя объект заблокированным и потенциально приводящим к взаимоблокировкам в будущем. И даже если вы не используете исключения, вы можете использовать какой-нибудь библиотечный код, который вы используете в performOperation. Или вы можете ошибочно когда-нибудь в будущем вставить возврат и забыть разблокировать все принадлежащие блокировки до и так далее ...
То же самое касается вызовов pthread_mutex_lock и pthread_mutex_unlock.
Я бы рекомендовал использовать RAII для блокировки/разблокировки.
I.e. код может выглядеть так:
typedef std::unordered_map<string, classA*>MAP1;
MAP1 map1;
Lock mapLock(&mutexA); //automatci variable. The destructor of the Lock class
//automatically calls pthread_mutex_unlock in its destructor if it "owns" the
//mutex
if(map1.find(id) == map1.end())
{
classA* obj1 = new classA;
map1[id] = obj1;
Lock objLock(obj);
mapLock.release(); //we explicitly release mapLock here
obj1->field1 = 1;
performOperation(obj1); //takes some time
}
I.e. для ссылки на некоторую минималистичную поддержку резьбы RAAI см. «Современный дизайн C++: общее программирование и образцы дизайна» Андрея Александреску (see here). Другие ресурсы также существуют (here)
В конце концов я попытаюсь описать еще одну проблему, которую я вижу с кодом. Точнее, проблема, которую я вижу, имея методы getMutex и releaseMutex как методы и вызывающие их явно. Предположим, что поток 1 блокирует карту, создает объект вызова getMutex и разблокирует карту. Другой поток (позволяет называть его Thread 2) получает запланированные блокировки выполнения, карта получает итератор к map1 [id] объекта и вызывает releaseMutex() в pObject (т. Е. Предположим, из-за ошибки, код не пытается сначала вызовите getMutex). Теперь Thread 1 получает запланированный вызов и вызывает в какой-то момент releaseMutex(). Таким образом, объект был заблокирован один раз, но дважды выпущен. То, что я пытаюсь сказать, заключается в том, что это будет тяжелая работа, чтобы убедиться, что вызовы всегда правильно спарены перед исключениями, потенциальными ранними результатами, которые не разблокируются и неверно используют интерфейс блокировки объекта. Также Thread 2 может просто удалить pObject, полученный с карты, и удалить его с карты. Затем поток 1 продолжит работу с уже удаленным объектом.
При разумном использовании RAII код будет проще понять (даже короче, если вы сравните наши версии), а также поможет в решении некоторых проблем, перечисленных выше.
Я не вижу областей, где может возникнуть взаимоблокировка, если только каким-то образом другой поток не может попасть в obj1 до создания '-> getMutex()'. Другими словами, до тех пор, пока вы блокируете 'mutexA' перед доступом к' map1' для чтения или записи, вы должны быть в порядке. Тем не менее, я думаю, что я переместил бы 'pthread_mutex_unlock (& mutexA)' на одну строку, пока 'id' для карты не изменяется в зависимости от содержимого объектов в ней. – milkypostman
Доступны ли объекты, хранящиеся на вашей карте, через несколько потоков для обновлений в поле 1? Разве не лучше сделать объекты потокобезопасными сами по себе, а не загромождать --- извините за это слово --- ваш код с 'obj1-> getMutex'? – Jaywalker
, когда я работал с MSVC, несколько лет назад у нас была многопоточная версия стандартных библиотек. Если вы можете получить потокобезопасную версию stl, она может по крайней мере избавить вас от мьютекса «mutexA» в контейнере «mutexA» – davka