boost shared_mutex의 예 (여러 읽기 / 쓰기 1 회)?


116

일부 데이터를 자주 읽고 가끔 데이터가 업데이트되는 멀티 스레드 앱이 있습니다. 현재 뮤텍스는 해당 데이터에 대한 액세스를 안전하게 유지하지만 여러 스레드가 동시에 읽을 수 있고 업데이트가 필요할 때만 잠글 수 있기 때문에 비용이 많이 듭니다 (업데이트 스레드는 다른 스레드가 완료 될 때까지 기다릴 수 있음). .

이것이 boost::shared_mutex해야 할 일 이라고 생각 하지만 사용 방법이 명확하지 않고 명확한 예를 찾지 못했습니다.

시작하는 데 사용할 수있는 간단한 예제가있는 사람이 있습니까?


1800 정보의 예가 맞습니다. 이 문서도 참조하십시오 : Boost Threads의 새로운 기능 .
Assaf Lavie

답변:


102

다음과 같이 할 것 같습니다.

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
}

7
이번이 처음으로 부스트를 사용하고 있고 저는 C ++ 초보자이므로 제가 놓친 것이있을 수 있습니다.하지만 제 코드에서 다음과 같이 유형을 지정해야했습니다. boost :: shared_lock <shared_mutex> lock (_접속하다);
Ken Smith

2
직접 사용하려고하는데 오류가 발생합니다. '잠금'앞에 템플릿 인수가 없습니다. 어떤 아이디어?
Matt

2
@shaz 범위가 지정되어 있지만 필요한 경우 .unlock ()을 사용하여 일찍 릴리스 할 수 있습니다.
mmocny 2011 년

4
누락 된 템플릿 인수를 추가했습니다.

1
@raaj you can get the upgrade_lock, but upgrade to a unique lock will block until the shared_lock is release
1800 INFORMATION

166

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을 획득 할 수 있습니다 (만나면 어색하다고 생각했습니다). 따라서 모든 독자가 조건부 작성자 인 경우 다른 솔루션을 찾아야합니다.


1
"다른 해결책"에 대해 언급하기 위해. 조건부 작성자가있는 모든 독자가 항상 shared_lock을 얻도록하고 권한을 작성하기 위해 업그레이드해야 할 때 독자 잠금을 .unlock ()하고 새로운 unique_lock을 얻습니다. 이로 인해 응용 프로그램의 논리가 복잡해지며 이제 다른 작성자가 처음 읽을 때부터 상태를 변경할 수있는 기회가 생깁니다.
mmocny 2011 년

8
줄이 _access를boost::unique_lock< boost::shared_mutex > lock(lock); 읽어야 하지 boost::unique_lock< boost::shared_mutex > lock( 않습니까 ); ?
SteveWilkinson 2011

4
마지막 경고는 매우 이상합니다. 한 번에 하나의 스레드 만 upgrade_lock을 보유 할 수있는 경우 upgrade_lock과 unique_lock의 차이점은 무엇입니까?
Ken Smith

2
@Ken 나는 그다지 명확하지 않았지만 upgrade_lock의 이점은 현재 일부 shared_lock이 획득 되어도 차단되지 않는다는 것입니다 (적어도 고유하게 업그레이드 할 때까지). 그러나 첫 번째 스레드가 고유 한 것으로 업그레이드되지 않았더라도 upgrade_lock을 시도하고 획득하는 두 번째 스레드는 차단됩니다.
mmocny 2011 년

6
이것은 알려진 부스트 문제입니다. 부스트 1.50 베타 버전에서 해결 될 것 같다 svn.boost.org/trac/boost/ticket/5516
Ofek Shilon에게

47

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;

5
나는 또한 말할 typedef boost::unique_lock< Lock > WriteLock; typedef boost::shared_lock< Lock > ReadLock;것이다.
vines

6
전체 thread.hpp를 포함 할 필요는 없습니다. 자물쇠 만 필요한 경우 자물쇠를 포함하십시오. 내부 구현이 아닙니다. 포함을 최소한으로 유지하십시오.
Yochai Timmer 2014

5
확실히 가장 간단한 구현이지만 뮤텍스와 잠금을 모두 잠금이라고 부르는 것은 혼란 스럽습니다. 뮤텍스는 뮤텍스이고 잠금은 잠금 상태로 유지하는 것입니다.
Tim MB

17

좀 더 경험적인 정보를 추가하기 위해 업그레이드 가능한 잠금의 전체 문제와 boost shared_mutex (여러 읽기 / 한 쓰기)의 예를 조사하고 있습니까? 업그레이드되지 않은 경우에도 하나의 스레드 만 upgrade_lock을 가질 수 있다는 중요한 정보를 추가하는 좋은 대답입니다. 이는 공유 잠금을 먼저 해제하지 않고는 공유 잠금에서 고유 잠금으로 업그레이드 할 수 없다는 것을 의미하므로 중요합니다. (이것은 다른 곳에서 논의되었지만 가장 흥미로운 스레드는 여기 http://thread.gmane.org/gmane.comp.lib.boost.devel/214394입니다. )

그러나 잠금 업그레이드를 기다리는 스레드 (즉, 모든 독자가 공유 잠금을 해제 할 때까지 기다려야 함)와 동일한 것을 기다리는 작성자 잠금 (예 : unique_lock) 사이에 중요한 (문서화되지 않은) 차이점을 발견했습니다.

  1. shared_mutex에서 unique_lock을 기다리는 스레드는 들어오는 모든 새 리더를 차단하므로 작성자 요청을 기다려야합니다. 이것은 독자가 작가를 굶주 리지 않도록 보장합니다 (하지만 작가가 독자를 굶주릴 수 있다고 믿습니다).

  2. upgradeable_lock이 업그레이드되기를 기다리는 스레드는 다른 스레드가 공유 잠금을 얻을 수 있도록 허용하므로 독자가 매우 빈번한 경우이 스레드가 고갈 될 수 있습니다.

이것은 고려해야 할 중요한 문제이며 아마도 문서화되어야합니다.


3
Terekhov algorithm의 보장은 1., 작가는 독자를 굶어 수 없습니다. 참조 . 하지만 2.사실입니다. upgrade_lock은 공정성을 보장하지 않습니다. 참조 .
JonasVautherin 2014

2

판독기 수와 동일한 수의 세마포어를 사용하십시오. 각 독자가 읽기 위해 세마포어의 한 카운트를 가져 와서 동시에 읽을 수 있도록합니다. 그런 다음 작가가 쓰기 전에 모든 세마포어 카운트를 가져 오도록합니다. 이로 인해 작성기는 모든 읽기가 완료 될 때까지 기다린 다음 쓰는 동안 읽기를 차단합니다.


(1) 작가가 원자 적으로 임의의 양만큼 카운트를 감소시키는 방법은 무엇입니까? (2) 작가가 어떻게 든 카운트를 0으로 줄이면 이미 실행중인 독자가 쓰기 전에 끝날 때까지 어떻게 기다릴까요?
Ofek Shilon

나쁜 생각 : 두 명의 작성자가 동시에 액세스하려고하면 교착 상태가 발생할 수 있습니다.
Caduchon

2

Jim Morris의 큰 반응, 나는 이것을 우연히 발견했고 알아내는 데 시간이 걸렸습니다. 다음은 unique_lock 부스트 (버전 1.54)에 대한 "요청"을 제출 한 후 모든 shared_lock 요청을 차단하는 것을 보여주는 간단한 코드입니다. unique_lock과 upgradeable_lock 중 하나를 선택하면 쓰기 우선 순위를 원하는지 또는 우선 순위가 없는지 선택할 수 있다는 점이 매우 흥미 롭습니다.

또한 (1) Jim Morris의 게시물에서 이것이 모순되는 것 같습니다 : 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();
}

실제로 [ stackoverflow.com/questions/12082405/… 의 코드가 작동 하는 동안 위 코드가 교착 상태가되는 이유를 파악하는 데 문제가 있습니다.
dale1209 2013

1
(2)가 (1)이 잠금을 해제하기를 기다리고 있기 때문에 실제로 (3)이 아니라 (2)에서 교착 상태가됩니다. 기억하십시오 : 고유 한 잠금을 얻으려면 기존의 모든 공유 잠금이 완료 될 때까지 기다려야합니다.
JonasVautherin 2014

@JonesV, (2) 모든 공유 잠금이 완료 될 때까지 기다리더라도 (1)을 획득 한 스레드와 다른 스레드이기 때문에 교착 상태가 아닙니다. (3) 줄이 존재하지 않으면 프로그램은 교착 상태없이 완료하십시오.
SagiLow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.