Нет.Как указал Diemtar Kühl, вам нужен замок на контейнере, который должен быть получен при каждом доступе к контейнеру.Таким образом, ваш сценарий для большинства доступа будет (C
это блокировка контейнера, O
блокировка для отдельного объекта:
acquire C
find object
acquire O
release C
process object
release O
и, если вы удалите, либо:
acquire C
find object
acquire O
delete object
release O
release C
, или если вам нужно обработать сначала, прежде чем принять решение об удалении:
acquire C
find object
acquire O
release C
process, determine that deletion is needed
acquire C
release O
release C
Это создает несколько проблем. Наиболее очевидным является то, что RAII не может использоваться для управления блокировками, по крайней мере, не в естественном порядке. (Он по-прежнему может использоваться для обеспечения освобождения блокировок в случае исключения, но снятие блокировки контейнера в первом сценарии должно быть ручным.) Что более важно, он может быть заблокирован как минимум в двух случаях:
Если вашим потокам требуется одновременный доступ к нескольким объектам. В этом случае в первом сценарии у вас есть поток 1, который получает C, затем получает O1, а затем освобождает CПосле этого поток 2 получает C, затем блокирует на O1. Поток 1 затем возобновляет работу и решает, что ему также необходим доступ к объекту 2. Таким образом, он пытается получить C и блокирует, ожидаяд 2 отпустить.(Поток 2, конечно, блокируется, пока поток 1 не освободит O1.)
Если вы используете второй сценарий для удаления, то достаточно, чтобы второй поток попытался получить доступ кобъект, над которым вы работаете, пока вы его обрабатываете.Как и выше, второй поток блокируется на O (который удерживает первый поток), а первый поток блокируется на C (второй сценарий получает C в сценарии).И ни один из потоков никуда не денется, оба ожидают продолжения другого.
Если ни один из потоков не блокирует более одного объекта и первый сценарий используется для удаления, шаблон будетРабота.Но это очень хрупко - слишком легко представить программиста по обслуживанию, нарушающего одно из этих условий, - и я настоятельно рекомендую против этого.(Конечно, если ни одна из альтернатив не обеспечивает достаточную пропускную способность, вам, возможно, придется ее использовать. И даже второй сценарий удаления можно заставить работать , если вы отпускаете O перед попыткой второго получения C,затем снова получите O, если у вас есть C. Основные условия: вы всегда должны приобретать C, затем O в этом порядке и никогда не пытаться приобрести C, когда у вас есть O.)
Также обратите внимание, чтоНаличие каждого объекта, содержащего мьютекс, может быть непростым делом, так как вы должны удерживать мьютекс до тех пор, пока объект не будет удален с карты.Это означает, что либо сама карта содержит указатель на объекты (и сохраняет указатель на объект после удаления его с карты и освобождает блокировку и удаляет объект через этот указатель), либо объект сохраняет указатель на объект.mutex.
Самое простое решение - просто использовать одну блокировку на C и поддерживать ее во время обработки O. Если обработка не слишком длинная, это может быть приемлемо;если вы используете многопоточность по той причине, что можете обрабатывать несколько ядер одновременно, это не сработает.
В противном случае вы можете рассмотреть возможность использования rwlock для контейнера и удержанияв течение всего времени, в течение которого вы удерживаете O. Затем можно продолжить простой доступ, поскольку это только доступ для чтения, а блокировка допускает множественный доступ для чтения.Для удаления вам понадобится доступ на запись, который будет блокироваться до тех пор, пока не завершатся все доступы на чтение;вам также все еще понадобится специальная обработка для второго сценария удаления, поскольку попытка обновить доступ с чтения на запись может привести к взаимоблокировке, как описано выше.(Чтобы перейти от чтения к записи, необходимо, чтобы никакой другой поток не имел права на чтение.)