Пример для повышения shared_mutex (многократное чтение / одна запись)?
У меня есть многопоточное приложение, которое часто читает некоторые данные, и иногда эти данные обновляются. Прямо сейчас мьютекс обеспечивает безопасный доступ к этим данным, но это дорого, потому что я хотел бы, чтобы несколько потоков могли читать одновременно, и блокировал их только тогда, когда требуется обновление (поток обновления может ожидать завершения других потоков),
Я думаю это то что boost::shared_mutex
должен делать, но я не понимаю, как его использовать, и не нашел четкого примера.
У кого-нибудь есть простой пример, который я мог бы использовать, чтобы начать?
6 ответов
Похоже, вы бы сделали что-то вроде этого:
boost::shared_mutex _access;
void reader()
{
// get shared access
boost::shared_lock<boost::shared_mutex> lock(_access);
// now we have shared access
}
void writer()
{
// get upgradable access
boost::upgrade_lock<boost::shared_mutex> lock(_access);
// get exclusive access
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
// now we have exclusive access
}
1800 ИНФОРМАЦИЯ более или менее правильная, но есть несколько вопросов, которые я хотел исправить.
boost::shared_mutex _access;
void reader()
{
boost::shared_lock< boost::shared_mutex > lock(_access);
// do work here, without anyone having exclusive access
}
void conditional_writer()
{
boost::upgrade_lock< boost::shared_mutex > lock(_access);
// do work here, without anyone having exclusive access
if (something) {
boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock);
// do work here, but now you have exclusive access
}
// do more work here, without anyone having exclusive access
}
void unconditional_writer()
{
boost::unique_lock< boost::shared_mutex > lock(_access);
// do work here, with exclusive access
}
Также обратите внимание, что в отличие от shared_lock, только один поток может получить upgrade_lock за один раз, даже если он не обновлен (что я считал неудобным, когда столкнулся с ним). Итак, если все ваши читатели являются условными авторами, вам нужно найти другое решение.
Вы можете использовать boost для создания блокировки чтения-записи:
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
Lock myLock;
void ReadFunction()
{
ReadLock r_lock(myLock);
//Do reader stuff
}
void WriteFunction()
{
WriteLock w_lock(myLock);
//Do writer stuff
}
Просто чтобы добавить больше эмпирической информации, я исследовал всю проблему обновляемых блокировок и Пример для повышения shared_mutex (многократное чтение / одна запись)? является хорошим ответом, добавляя важную информацию о том, что только один поток может иметь upgrade_lock, даже если он не обновлен, это важно, поскольку это означает, что вы не можете перейти с общей блокировки на уникальную блокировку, не сняв сначала общую блокировку. (Это обсуждалось в другом месте, но самая интересная тема находится здесь http://thread.gmane.org/gmane.comp.lib.boost.devel/214394)
Однако я обнаружил важное (недокументированное) различие между потоком, ожидающим обновления до блокировки (т. Е. Нужно подождать, пока все читатели освободят общую блокировку), и блокировкой записи, ожидающей одного и того же (то есть unique_lock).
Поток, ожидающий unique_lock в shared_mutex, блокирует поступление новых читателей, они должны ждать запроса писателей. Это гарантирует, что читатели не будут голодать писателей (однако я считаю, что писатели могут голодать читателей).
Поток, который ожидает обновления upgradeable_lock, позволяет другим потокам получить общую блокировку, поэтому этот поток может быть истощен, если читатели очень часты.
Это важный вопрос для рассмотрения, и, вероятно, должен быть задокументирован.
Используйте семафор со счетчиком, равным количеству читателей. Пусть каждый читатель возьмет один счет семафора, чтобы прочитать, таким образом, все они могут читать одновременно. Затем позвольте автору взять все значения семафора до записи. Это заставляет автора ждать завершения всех чтений, а затем блокировать чтения во время записи.
Отличный ответ Джима Морриса, я наткнулся на это, и мне потребовалось некоторое время, чтобы понять. Вот некоторый простой код, который показывает, что после отправки "запроса" на повышение уникальности (версия 1.54) блокирует все запросы shared_lock. Это очень интересно, так как мне кажется, что выбор между unique_lock и upgradeable_lock позволяет нам, если мы хотим записать приоритет или нет приоритета.
Также (1) в посте Джима Морриса, кажется, противоречит этому: Boost shared_lock. Читать предпочтительнее?
#include <iostream>
#include <boost/thread.hpp>
using namespace std;
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > UniqueLock;
typedef boost::shared_lock< Lock > SharedLock;
Lock tempLock;
void main2() {
cout << "10" << endl;
UniqueLock lock2(tempLock); // (2) queue for a unique lock
cout << "11" << endl;
boost::this_thread::sleep(boost::posix_time::seconds(1));
lock2.unlock();
}
void main() {
cout << "1" << endl;
SharedLock lock1(tempLock); // (1) aquire a shared lock
cout << "2" << endl;
boost::thread tempThread(main2);
cout << "3" << endl;
boost::this_thread::sleep(boost::posix_time::seconds(3));
cout << "4" << endl;
SharedLock lock3(tempLock); // (3) try getting antoher shared lock, deadlock here
cout << "5" << endl;
lock1.unlock();
lock3.unlock();
}