Пример повышения общего_мутекса (несколько операций чтения/одна запись)?

StackOverflow https://stackoverflow.com/questions/989795

Вопрос

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

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

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

Это было полезно?

Решение

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

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

Начиная с 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;

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

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

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

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

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

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

Отличный ответ Джима Морриса, я наткнулся на это, и мне потребовалось некоторое время, чтобы понять.Вот простой код, который показывает, что после отправки «запроса» на повышение unique_lock (версия 1.54) блокируются все запросы Shared_lock.Это очень интересно, поскольку мне кажется, что выбор между unique_lock и updateable_lock позволяет нам выбрать приоритет записи или отсутствие приоритета.

Также (1) в сообщении Джима Морриса, кажется, противоречит этому:Увеличьте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();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top