Подтвердить что ты не робот

Пример для boost shared_mutex (многократное чтение/запись)?

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

Я думаю, что это то, что должен делать boost::shared_mutex, но я не понимаю, как его использовать, и не нашел ясного примера.

Есть ли у кого-нибудь простой пример, который я мог бы использовать для начала?

4b9b3361

Ответ 1

Похоже, вы сделали бы что-то вроде этого:

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
}

Ответ 2

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, только один поток может одновременно приобретать update_lock, даже если он не обновлен (что я считал неудобным, когда сталкивался с ним). Итак, если все ваши читатели являются условными писателями, вам нужно найти другое решение.

Ответ 3

Начиная с C++ 17 (VS2015) вы можете использовать стандарт для блокировки чтения-записи:

#include <shared_mutex>

typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock > WriteLock;
typedef std::shared_lock< Lock > ReadLock;

Lock myLock;


void ReadFunction()
{
    ReadLock r_lock(myLock);
    //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}

Для более старой версии вы можете использовать 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;

Ответ 4

Чтобы добавить еще некоторую эмпирическую информацию, я изучал весь вопрос об обновляемых замках, а Пример для boost shared_mutex (несколько чтений/одна запись)? хороший ответ, добавляющий важную информацию о том, что только один поток может иметь upgrade_lock, даже если он не обновлен, что важно, так как это означает, что вы не можете выполнить обновление с общей блокировки до уникальной блокировки, не отпуская сначала общую блокировку. (Это обсуждалось в другом месте, но наиболее интересная тема здесь http://thread.gmane.org/gmane.comp.lib.boost.devel/214394)

Однако я нашел важную (недокументированную) разницу между потоком, ожидающим обновления до блокировки (т.е. должен ждать, пока все читатели освободят общую блокировку), и блокировка записи, ожидающая того же самого (т.е. unique_lock).

  • Нить, ожидающая уникального_блока на shared_mutex, блокирует появление новых читателей, им приходится ждать, пока запрос авторов. Это позволяет читателям не голодать писателям (однако я считаю, что писатели могут голодать читателям).

  • Нить, ожидающая обновления upgradeable_lock для обновления, позволяет другим потокам получать общую блокировку, поэтому этот поток может быть голоден, если читатели очень часты.

Это важная проблема для рассмотрения и, вероятно, должна быть документирована.

Ответ 5

Используйте семафор со счетом, равным числу читателей. Пусть каждый читатель берет один счет семафора, чтобы читать, таким образом, они могут все читать одновременно. Затем пусть писатель берет ВСЕ семафорные счета до написания. Это заставляет писателя ждать завершения всех чтений и затем блокировки чтения во время записи.

Ответ 6

Отличный ответ Джима Морриса, я наткнулся на это, и мне потребовалось некоторое время, чтобы понять. Вот простой код, который показывает, что после отправки "запроса" для boost_lock boost (версия 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();
}