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.