Стандартный способ сделать объекты STL потокобезопасными? - PullRequest
4 голосов
/ 05 июня 2011

Мне нужно несколько контейнеров STL, threadsafe.

По сути, я думал, что мне просто нужно добавить 2 метода к каждому из объектов контейнера STL,

.lock()

.unlock()

Я также могу разбить его на

.lockForReading()
.unlockForReading()
.lockForWriting()
.unlockForWriting()

Допустимо любое число блокировок для параллельного чтения, но если есть блокировка для записи, то чтение и запись блокируются.

Попытка блокировки длязапись ждет, пока семафор lockForReading не упадет до 0.

Есть ли стандартный способ сделать это?

Как я планирую сделать это неправильно или недальновидно?

Ответы [ 4 ]

6 голосов
/ 05 июня 2011

Это действительно плохо. Внешний код не распознает и не поймет вашу семантику потоков, а легкость доступности псевдонимов для объектов в контейнерах делает их плохими интерфейсами, безопасными для потоков.

Потокобезопасность возникает при расчетном времени. Вы не можете решить проблему безопасности потоков, бросая замки на проблему. Вы решаете проблему безопасности потоков, не имея двух потоков, одновременно записывающих одни и те же данные - в общем случае, конечно. Тем не менее, не несет ответственность за конкретный объект для обеспечения безопасности потоков, за исключением примитивов прямой потоковой синхронизации.

Вы можете иметь параллельные контейнеры, предназначенные для одновременного использования. Однако их интерфейсы значительно отличаются от того, что предлагают стандартные контейнеры. Например, меньше псевдонимов для объектов в контейнере и каждой отдельной операции.

6 голосов
/ 05 июня 2011

Стандартный способ сделать это - получить блокировку в конструкторе и освободить ее в деструкторе. Это чаще всего известно как Resource Acquisition Is Initialization или RAII. Я настоятельно рекомендую вам использовать эту методологию, а не

.lock()

.unlock()

Что не является исключением исключений. Вы можете легко забыть разблокировать мьютекс перед броском, что приведет к тупику при следующей попытке блокировки.

Существует несколько типов синхронизации в библиотеке Boost.Thread, которые будут вам полезны, , в частности boost::mutex::scoped_lock. Вместо добавления методов lock() и unlock() в любой контейнер, к которому вы хотите получить доступ несколько потоков, я предлагаю вам использовать boost:mutex или его эквивалент и создавать экземпляр boost::mutex::scoped_lock при доступе к контейнеру.

2 голосов
/ 05 июня 2011

Есть ли стандартный способ сделать это?

Нет, и для этого есть причина.

Вот как я планирую это сделать неправильно или недальновидно?

Не обязательно неправильно синхронизировать доступ к одному объекту контейнера, но интерфейс класса контейнера очень часто является неправильным местом для синхронизации (, как DeadMG говорит : псевдонимы объектов и т. д.).

Лично я думаю, что и TBB , и такие вещи, как concurrent_vector , могут быть излишними или все же неправильными инструментами для "простой" проблемы синхронизации.

Я считаю, что достаточно просто добавить (закрытый) объект Lock (к классу, содержащему контейнер) и обернуть 2 или 3 шаблона доступа к одному объекту контейнера, и его будет намного легче понять и поддерживать для других по дороге.

0 голосов
/ 05 июня 2011

Сэм : вам не нужен метод .lock(), потому что что-то может пойти не так, что препятствует вызову метода .unlock() в конце блока, но если .unlock() вызывается как следствием уничтожения объекта выделенной в стеке переменной является то, что любой вид раннего возврата из функции, которая вызывает .lock(), будет гарантированно освобождать блокировку.

DeadMG : Intel Threading Building Blocks ( с открытым исходным кодом ) может быть тем, что вы ищете.

Также есть Microsoft concurrent_vector и concurrent_queue , которые уже поставляются с Visual Studio 2010.

...