std :: unique_lock <std :: mutex> 또는 std :: lock_guard <std :: mutex>?


349

두 가지 사용 사례가 있습니다.

A. 두 스레드의 액세스를 대기열에 동기화하고 싶습니다.

B. 두 스레드의 액세스를 큐에 동기화하고 스레드 중 하나가 다른 스레드에 의해 큐에 저장 될 내용을 기다리는 조건 변수를 사용하려고합니다.

사용 사례 AI의 경우를 사용하는 코드 예를 참조하십시오 std::lock_guard<>. 사용 사례 BI의 경우를 사용하는 코드 예를 참조하십시오 std::unique_lock<>.

둘 중 어느 것과 어떤 유스 케이스에서 사용해야하는지의 차이점은 무엇입니까?

답변:


344

차이점은를 잠그고 잠금을 해제 할 수 있다는 것 std::unique_lock입니다. std::lock_guard건설시 한 번만 잠기고 파괴시 잠금이 해제됩니다.

따라서 유스 케이스 B의 경우 std::unique_lock조건 변수에 반드시 a가 필요 합니다. A의 경우 가드를 다시 잠글 지 여부에 따라 다릅니다.

std::unique_lock뮤텍스를 즉시 잠그지 않고 RAII 래퍼를 빌드하도록 구성 할 수있는 다른 기능이 있습니다 ( 여기 참조 ).

std::lock_guard또한 편리한 RAII 래퍼를 제공하지만 여러 뮤텍스를 안전하게 잠글 수는 없습니다. 제한된 범위에 대한 랩퍼가 필요할 때 사용할 수 있습니다 (예 : 멤버 함수).

class MyClass{
    std::mutex my_mutex;
    void member_foo() {
        std::lock_guard<mutex_type> lock(this->my_mutex);            
        /*
         block of code which needs mutual exclusion (e.g. open the same 
         file in multiple threads).
        */

        //mutex is automatically released when lock goes out of scope           
};

기본적으로 chmike으로 질문을 명확히하기 std::lock_guardstd::unique_lock동일합니다. 따라서 위의 경우로 대체 할 수 std::lock_guard있습니다 std::unique_lock. 그러나 std::unique_lock약간 더 많은 오버 헤드가있을 수 있습니다.

요즘에는 std::scoped_lock대신에 사용해야 합니다 std::lock_guard.


2
명령어 std :: unique_lock <std :: mutex> lock (myMutex); 뮤텍스가 생성자에 의해 잠겨 있습니까?
chmike

3
@chmike 그렇습니다. 설명이 추가되었습니다.
Stephan Dollberg

10
@chmike 글쎄, 기능보다는 효율성에 대한 문제가 아니라고 생각합니다. 경우 std::lock_guard귀하의 경우 A에 대해 충분하다, 당신은 그것을 사용해야합니다. 불필요한 경비를 피할뿐만 아니라이 가드를 절대 잠금 해제하지 않을 것이라는 독자의 의도를 보여줍니다.
Stephan Dollberg

5
@chmike : 이론적으로 그렇습니다. 그러나 Mutices는 가벼운 구조가 아니기 때문에 unique_lock실제로 Mutex를 잠그고 잠금을 해제하는 비용으로 인해 컴파일러 의 추가 오버 헤드가 줄어들 수 있습니다 (컴파일러가 해당 오버 헤드를 최적화하지 않은 경우 가능할 수 있음).
그리즐리

6
So for usecase B you definitely need a std::unique_lock for the condition variable-예 , 그러나 스레드에서 만 cv.wait()해당 메소드가 원자 적으로 뮤텍스를 해제하기 때문입니다. 당신이 공유 변수 (들)을 업데이트 한 다음 전화를 다른 스레드에서 cv.notify_one()간단한 lock_guard당신이 더 많은 내가 상상할 수없는 정교한 아무것도하지 않는 한 접미사는 ...에-범위 뮤텍스를 잠급니다! 예 : en.cppreference.com/w/cpp/thread/condition_variable- 저에게 효과적입니다 :)
underscore_d

115

lock_guard그리고 unique_lock거의 같은 것입니다; lock_guard인터페이스가 제한된 제한된 버전입니다.

A는 lock_guard항상 건축에서 파괴까지 잠금을 유지합니다. A unique_lock는 즉시 잠그지 않고 생성 할 수 있으며, 존재하는 어느 시점에서나 잠금 해제 할 수 있으며, 잠금 소유권을 한 인스턴스에서 다른 인스턴스로 이전 할 수 있습니다.

따라서 lock_guard의 기능이 필요하지 않으면 항상을 사용 하십시오 unique_lock. A가 condition_variable필요합니다 unique_lock.


11
A condition_variable needs a unique_lock.- 예 그러나 단지에 wait()ING 측, INF에 내 댓글에 자세히 설명한다.
underscore_d

48

를 파괴하지 않고 사이 lock_guard에 수동으로 unlock뮤텍스 를 만들 수 있어야하는 경우가 아니면 사용하십시오 lock.

특히 condition_variable에 호출 할 때 절전 모드로 전환 할 때 뮤텍스를 잠금 해제합니다 wait. 이것이 lock_guard여기에서 충분하지 않은 이유 입니다.


어떤 이유로 든 대기가 종료 될 때 뮤텍스가 항상 다시 획득되므로 조건부 변수의 대기 메소드 중 하나에 lock_guard를 전달하는 것이 좋습니다. 그러나 표준은 unique_lock에 대한 인터페이스 만 제공합니다. 이는 표준의 결함으로 간주 될 수 있습니다.
Chris Vine

3
@Chris이 경우에도 캡슐화가 중단됩니다. wait 메소드는 뮤텍스를 추출하여 lock_guard잠금 해제 할 수 있어야 하므로 가드의 클래스 불변성을 일시적으로 중단시킵니다. 이것이 사용자에게는 보이지 않지만이 경우에는 사용을 허용하지 않는 합당한 이유를 고려할 것 lock_guard입니다.
ComicSansMS

그렇다면 보이지 않고 감지 할 수 없습니다. gcc-4.8이 그렇게합니다. wait (unique_lock <mutex> &)는 __gthread_cond_wait (& _ M_cond, __lock.mutex ()-> native_handle ())를 호출합니다 (libstdc ++-v3 / src / c ++ 11 / condition_variable.cc 참조). pthread_cond_wait ()를 호출합니다 (libgcc 참조). /gthr-posix.h). lock_guard에 대해서도 동일한 작업을 수행 할 수 있습니다 (단, condition_variable의 표준이 아니기 때문은 아닙니다).
Chris Vine

4
@Chris 요점은 lock_guard기본 뮤텍스를 전혀 검색 할 수 없다는 것입니다. 이는 lock_guard을 사용하는 코드와 달리 사용하는 코드 에 대한 간단한 추론을 허용하기 위해 의도적으로 제한됩니다 unique_lock. 요청한 것을 달성하는 유일한 방법은 의도적으로 lock_guard클래스 캡슐화를 해제 하고 구현을 다른 클래스 (이 경우에는 condition_variable)에 노출시키는 것 입니다. 이는 두 잠금 유형의 차이를 기억할 필요가없는 조건 변수의 사용자에게 의심 할만한 이점을 지불하기에 힘든 가격입니다.
ComicSansMS

4
@Chris 어디에서 condition_variable_any.wait작동 하는 아이디어를 얻었 lock_guard습니까? 이 표준은 제공된 잠금 유형이 BasicLockable요구 사항 (§30.5.2) 을 충족하도록 요구하지만 lock_guard그렇지 않습니다. 기본 뮤텍스만이 가능하지만 이전에 지적한 이유로 lock_guard뮤텍스에 대한 액세스를 제공하지 않습니다.
ComicSansMS

11

사이의 공통된 것들이있다 lock_guardunique_lock특정 차이.

그러나 질문의 ​​맥락에서 컴파일러는 lock_guard조건 변수와 함께 조합을 사용할 수 없습니다 . 스레드 호출이 조건 변수를 기다릴 때 뮤텍스가 자동으로 잠금 해제되고 다른 스레드 / 스레드가 통지하고 현재 스레드를 호출되면 (대기에서 나옴) 잠금이 다시 획득됩니다.

이 현상은의 원칙에 위배 lock_guard됩니다. lock_guard한 번만 구성하고 한 번만 파괴 할 수 있습니다.

따라서, lock_guard조건 변수와 함께 사용되지 않을 수 있지만,이 unique_lock(때문 될 수 unique_lock잠겨 수회 해제 할 수있다).


5
he compiler does not allow using a lock_guard in combination with a condition variable이것은 거짓입니다. 그것은 확실히 않습니다 허용하고 완벽하게 작업 lock_guard상의 notify()보내고 쪽. 조건을 확인하는 동안 잠금을 해제해야 하므로 wait()int 측 에만을 필요로 합니다. unique_lockwait()
underscore_d

0

그것들은 실제로 같은 뮤텍스가 아니며, lock_guard<muType>거의 동일하다 std::mutex. 수명이 스코프의 끝에서 끝나는 것 (D-tor)은이 두 뮤텍스에 대한 명확한 정의이다.

lock_guard<muType> 범위가 지정된 블록 동안 뮤텍스를 소유하는 메커니즘이 있습니다.

unique_lock<muType> 지연된 잠금, 잠금에 대한 시간 제약 된 시도, 재귀 잠금, 잠금 소유권 이전 및 조건 변수와 함께 사용할 수있는 래퍼입니다.

다음은 구현 예입니다.

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <chrono>

using namespace std::chrono;

class Product{

   public:

       Product(int data):mdata(data){
       }

       virtual~Product(){
       }

       bool isReady(){
       return flag;
       }

       void showData(){

        std::cout<<mdata<<std::endl;
       }

       void read(){

         std::this_thread::sleep_for(milliseconds(2000));

         std::lock_guard<std::mutex> guard(mmutex);

         flag = true;

         std::cout<<"Data is ready"<<std::endl;

         cvar.notify_one();

       }

       void task(){

       std::unique_lock<std::mutex> lock(mmutex);

       cvar.wait(lock, [&, this]() mutable throw() -> bool{ return this->isReady(); });

       mdata+=1;

       }

   protected:

    std::condition_variable cvar;
    std::mutex mmutex;
    int mdata;
    bool flag = false;

};

int main(){

     int a = 0;
     Product product(a);

     std::thread reading(product.read, &product);
     std::thread setting(product.task, &product);

     reading.join();
     setting.join();


     product.showData();
    return 0;
}

이러한 예에서, I는를 사용 unique_lock<muType>하여condition variable


-5

다른 사람들이 언급했듯이 std :: unique_lock은 뮤텍스의 잠금 상태를 추적하므로 잠금 생성 후까지 잠금을 연기하고 잠금이 파괴되기 전에 잠금을 해제 할 수 있습니다. std :: lock_guard는 이것을 허용하지 않습니다.

std :: condition_variable 대기 함수가 고유 한 잠금뿐만 아니라 lock_guard를 가져 가지 않아야 할 이유가 없습니다. 대기가 종료 될 때마다 (어떤 이유로 든) 뮤텍스가 자동으로 다시 획득되어 의미 위반이 발생하지 않기 때문입니다. 그러나 표준에 따르면 조건 변수와 함께 std :: lock_guard를 사용하려면 std :: condition_variable 대신 std :: condition_variable_any를 사용해야합니다.

편집 : 삭제 "pthreads 인터페이스 std :: condition_variable과 std :: condition_variable_any는 동일해야합니다." gcc의 구현을 살펴보면 다음과 같습니다.

  • std :: condition_variable :: wait (std :: unique_lock &)은 unique_lock이 보유한 뮤텍스와 관련하여 기본 pthread 조건 변수에서 pthread_cond_wait ()를 호출합니다 (따라서 lock_guard에 대해 동일하게 수행 할 수는 있지만 표준이 아니기 때문에) 그것을 제공하지 않습니다)
  • std :: condition_variable_any는 뮤텍스 잠금이 아닌 객체를 포함하여 모든 잠금 가능 객체에서 작동 할 수 있습니다 (따라서 프로세스 간 세마포어로도 작동 할 수 있음)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.