2015-06-17 7 views
7

Я создаю многопоточную программу C++ с использованием pthread (стандарт C++ 98).Синхронизация потоков для C++-карты

У меня есть std :: map, к которой будут обращаться несколько потоков. Доступ будет добавлять и удалять элементы, используя find, а также доступ к элементам с помощью оператора [].

Я понимаю, что чтение с использованием оператора [] или даже изменение элементов с ним является потокобезопасным, но остальные операции не являются.

Первый вопрос: правильно ли я это понял?

Некоторые потоки будут просто получать доступ к элементам через [], в то время как другие будут выполнять некоторые другие операции. Очевидно, мне нужна некоторая форма синхронизации потоков.

Способ, которым я вижу это, должен работать: - Пока на карте не выполняется операция «запись», потоки должны быть способны «читать» из нее одновременно. - Когда поток хочет «записать» на карту, он должен установить блокировку, поэтому нить не запускает какую-либо операцию «читать» или «писать», а затем она должна ждать завершения всех операций «чтения», после чего он выполнит операцию и освободит блокировки. - После того, как замки были выпущены, все потоки должны быть в состоянии читать свободно.

Главный вопрос: какие методы синхронизации потоков можно использовать для достижения такого поведения?

Я прочитал о мьютексе, условных переменных и семафорах, и, насколько я вижу, они не будут делать то, что мне нужно. Я знаком с мьютексом, но не с cond. переменных или семафоров.

Основная проблема, которую я вижу, заключается в том, что мне нужен способ блокировки потоков до тех пор, пока что-то не произойдет (операция записи заканчивается) без этих потоков, а затем блокирует все по очереди. Также мне нужно что-то вроде инвертированного семафора, который блокирует, когда счетчик больше 1, а затем просыпается, когда он равен 0 (т. Е. Операция чтения не выполняется).

Заранее спасибо.

P.S. Это мой первый пост. Просьба указать, что я делаю что-то неправильно!

+1

Вам просто нужен мьютекс для чтения-записи? –

+1

Вам нужен замок RW, попробуйте [этот пример.] (Http://stackoverflow.com/questions/989795/example-for-boost-shared-mutex-multiple-reads-one-write) – gbjbaanb

+0

Прекрасный первый пост. Вы не можете использовать [C++ 11] (http://en.cppreference.com/w/cpp/thread)? Это упростит ваш код и сделает его более портативным. В зависимости от характера элементов контейнера вам могут не понадобиться более тяжелые функции потока. Возможно, достаточно примитивов [sync] (http://en.cppreference.com/w/cpp/atomic). –

ответ

6

Я понимаю, что чтение с использованием оператора [], или даже изменение элементов с ним, является потокобезопасным, но остальные операции не являются.

Правильно ли я это понимаю?

Хорошо, что вы сказали, это не совсем так. Одновременные читатели могут использовать для доступа к []существующих элементов, или использовать другие const функций (например, find, size() ...) безопасно если нет никаких одновременных не- const операций, таких как erase или insert мутирует в map<>. Параллельные потоки могут изменять разные элементы, но если один поток изменяет элемент, у вас должна быть некоторая синхронизация, прежде чем другой поток попытается получить доступ или дополнительно изменить этот конкретный элемент.

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

Это не совсем так, как это работает ... для писателей, чтобы быть в состоянии «ждать, пока все„читать“операция завершена», читателю (ей) необходимо получить блокировку. Писатели ожидают, что тот же самый замок будет выпущен, и приобретет его сами, чтобы ограничить других читателей или писателей, пока они не закончили свое обновление и не выпустили его.

Какие способы синхронизации потоков можно использовать для достижения такого поведения?

Мьютекс действительно подходит, хотя вы часто получаете более высокую производительность от блокировки чтения-записи (что позволяет одновременным читателям, а также приоритизировать ожидающих авторов над новыми читателями). Связанные POSIX Threads функции включают в себя: pthread_rwlock_rdlock, pthread_rwlock_wrlock, pthread_rwlock_unlock и т.д ..

противопоставлять эти два подхода, с читателями и писателями с помощью мьютекса вы получаете сериализации что-то вроде этого:

THREAD ACTION 
reader1 pthread_mutex_lock(the_mutex) returns having acquired lock, and 
     thread starts reading data 
reader2 pthread_mutex_lock(the_mutex) "hangs", as blocked by reader1 
writer1 pthread_mutex_lock(the_mutex) hangs, as blocked by reader1 
reader1 pthread_mutex_unlock(the_mutex) -> releases lock 
NOTE: some systems guarantee reader2 will unblock before writer1, some don't 
reader2 blocked pthread_mutex_lock(the_mutex) returns having acquired lock, 
     and thread starts reading data 
reader1 pthread_mutex_lock(the_mutex) hangs, as blocked by reader2 
reader2 pthread_mutex_unlock(the_mutex) -> releases lock  
writer1 blocked pthread_mutex_lock(the_mutex) returns having acquired lock, 
     and thread starts writing and/or reading data 
writer1 pthread_mutex_unlock(the_mutex) -> releases lock  
reader1 blocked pthread_mutex_lock(the_mutex) returns having acquired lock, 
     and thread starts reading data 
...etc... 

С для чтения-записи (обратите внимание, что первые два считывателя работают одновременно):

THREAD ACTION 
reader1 pthread_rwlock_rdlock(the_rwlock) returns having acquired lock, and 
     thread starts reading data 
reader2 pthread_rwlock_rdlock(the_rwlock) returns having acquired lock, and 
     thread starts reading data 
writer1 pthread_rwlock_wrlock(the_rwlock) hangs, as blocked by reader1/2 
reader1 pthread_rwlock_unlock(the_rwlock) -> releases lock 
reader1 pthread_rwlock_rwlock(the_rwlock) hangs, as pending writer 
reader2 pthread_rwlock_unlock(the_rwlock) -> releases lock  
writer1 blocked pthread_rwlock_wrlock(the_rwlock) returns having acquired lock, 
     and thread starts writing and/or reading data 
writer1 pthread_rwlock_unlock(the_rwlock) -> releases lock  
reader1 blocked pthread_rwlock_rwlock(the_rwlock) returns having acquired lock, 
     and thread starts reading data 
...etc... 
+0

Блокировки чтения/записи действительно то, что мне нужно. Не знал о них! Благодарю. – Dan

+1

В зависимости от ситуации мультиверсионный параллелизм может быть вариантом, например, может быть сделан путем помещения 'shared_ptr' в карту, а не самого объекта. Читатели копируют общий указатель, а писатель атомарно заменяет его другим. Таким образом, объекты, которые читаются, переживают их существование на карте, пока они больше не нужны. Обратите внимание, что копирование вокруг общих указателей не является точно «бесплатным», это может быть быстрее или медленнее, чем блокировка чтения-записи, в зависимости от того, что вы делаете. – Damon

+0

@Damon: хорошее предложение - возможно, это отдельный ответ или комментарий непосредственно под вопрос ...? До вас, конечно. Приветствия. –

Смежные вопросы