Pergunta

Eu tenho um aplicativo com vários segmentos que tem de ler alguns dados, muitas vezes, e, ocasionalmente, os dados são atualizados. Agora um mutex mantém o acesso a esse seguro de dados, mas é caro porque eu gostaria vários segmentos para ser capaz de ler ao mesmo tempo, e apenas bloqueá-los para fora quando é necessária uma actualização (o segmento de atualização poderia esperar para os outros tópicos ao fim) .

Eu acho que isso é o que boost::shared_mutex é suposto fazer, mas eu não estou claro sobre como usá-lo, e não ter encontrado um exemplo claro.

Alguém tem um exemplo simples que eu poderia usar para começar?

Foi útil?

Solução

Parece que você faria algo assim:

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
}

Outras dicas

1800 INFORMATION é mais ou menos correto, mas há algumas questões que eu queria correta.

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
}

Também Nota, ao contrário de um shared_lock, apenas um único thread pode adquirir uma upgrade_lock de uma só vez, mesmo quando ele não está atualizado (que eu pensei que era estranho quando eu corri para ele). Então, se todos os seus leitores são escritores condicionais, você precisa encontrar outra solução.

Desde C ++ 17 (VS2015) você pode usar o padrão para bloqueios de leitura e escrita:

#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
}

Para a versão mais antiga, você pode usar impulso com a mesma sintaxe:

#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;

Só para acrescentar outras informações mais empírica, tenho vindo a investigar toda a questão de fechaduras atualizáveis, e Exemplo de impulso shared_mutex (várias leituras / um escrever)? é uma boa resposta adicionando a informação importante que apenas um segmento pode ter um upgrade_lock mesmo que não é atualizado, que é importante, pois significa que você não pode atualizar a partir de um bloqueio compartilhado para um bloqueio exclusivo, sem soltar o bloqueio compartilhado em primeiro lugar. (Isso foi discutido em outros lugares mas o segmento mais interessante é aqui http: / /thread.gmane.org/gmane.comp.lib.boost.devel/214394 )

No entanto, eu fiz encontrar uma diferença importante (indocumentados) entre um fio à espera de um upgrade para um bloqueio (ou seja, as necessidades de esperar por todos os leitores para liberar o bloqueio compartilhado) e um bloqueio de escritor esperando a mesma coisa (ou seja, uma unique_lock ).

  1. O thread que está esperando por um unique_lock nos blocos shared_mutex quaisquer novos leitores que entram, eles têm que esperar para os escritores solicitar. Isso garante leitores não morrer de fome escritores (porém eu acredito que os escritores poderiam morrer de fome leitores).

  2. O segmento que está aguardando uma upgradeable_lock para atualizar permite que outros segmentos para obter um bloqueio compartilhado, assim que esta discussão poderia ser fome se os leitores são muito frequentes.

Esta é uma questão importante a considerar, e provavelmente deve ser documentado.

Use um semáforo com uma contagem que é igual ao número de leitores. Que cada leitor tomar uma contagem do semáforo, a fim de ler, de que forma eles podem todos leitura ao mesmo tempo. Então deixe o escritor tomar todas as contagens de semáforo antes de escrever. Isso faz com que o escritor que esperar para todas as leituras para terminar e, em seguida, bloquear lê enquanto que a escrita.

Great resposta por Jim Morris, me deparei com isso e ele me levou um tempo para descobrir. Aqui está um código simples que mostra que depois de apresentar um "pedido" para um impulso unique_lock (versão 1.54) bloqueia todas as solicitações shared_lock. Isso é muito interessante, pois parece-me que a escolha entre unique_lock e upgradeable_lock permite que se quisermos prioridade de gravação ou nenhuma prioridade.

Além disso (1) no post de Jim Morris parece contradizer esta: . Leia preferido?

#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();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top