Пример для повышения shared_mutex (несколько чтений / одна запись)? - PullRequest
110 голосов
/ 13 июня 2009

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

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

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

Ответы [ 6 ]

161 голосов
/ 27 января 2011

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

98 голосов
/ 13 июня 2009

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

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
}
44 голосов
/ 23 июня 2011

Вы можете использовать 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
}
17 голосов
/ 15 марта 2011

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

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

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

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

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

2 голосов
/ 21 ноября 2013

Отличный ответ Джима Морриса, я наткнулся на это, и мне потребовалось некоторое время, чтобы понять. Вот простой код, который показывает, что после отправки «запроса» на повышение уникальности (версия 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();
}
2 голосов
/ 20 ноября 2010

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

...