425

我有两个用例。

A. 我想同步访问两个线程的队列。

B. 我想同步两个线程对队列的访问并使用条件变量,因为其中一个线程将等待另一个线程将内容存储到队列中。

对于用例 AI,请参阅使用std::lock_guard<>. 对于用例 BI,请参阅使用std::unique_lock<>.

两者之间有什么区别,我应该在哪个用例中使用哪一个?

4

7 回答 7

412

不同之处在于您可以锁定和解锁std::unique_lock. std::lock_guard只会在建造时锁定一次,在破坏时解锁。

因此,对于用例 B,您肯定需要一个std::unique_lock条件变量。情况 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_guardstd::unique_lock. 但是,std::unique_lock可能会有更多的开销。

请注意,这些天(自 C++17 起)应该使用std::scoped_lock而不是std::lock_guard.

于 2013-12-11T10:39:37.150 回答
148

lock_guard几乎unique_lock是一回事;lock_guard是具有受限界面的受限版本。

Alock_guard从它的构建到它的破坏总是持有一把锁。Aunique_lock可以在不立即锁定的情况下创建,可以在其存在的任何时候解锁,并且可以将锁的所有权从一个实例转移到另一个实例。

所以你总是使用lock_guard,除非你需要unique_lock. Acondition_variable需要一个unique_lock.

于 2013-12-11T10:41:08.947 回答
57

lock_guard除非您需要能够在unlock不破坏lock.

特别是,condition_variable在调用wait. 这就是为什么 alock_guard在这里是不够的。

如果您已经在使用 C++17 或更高版本,请考虑将其用作具有相同基本功能scoped_lock的稍微改进的版本。lock_guard

于 2013-12-11T10:39:03.923 回答
11

之间有一些共同点lock_guard,也有unique_lock一些区别。

但是在所问问题的上下文中,编译器不允许将 alock_guard与条件变量结合使用,因为当线程调用等待条件变量时,互斥锁会自动解锁,并且当其他线程/线程通知当前线程时被调用(退出等待),重新获取锁。

这种现象是违背的lock_guardlock_guard只能构造一次,只能破坏一次。

因此lock_guard不能与条件变量结合使用,但 aunique_lock可以(因为unique_lock可以多次锁定和解锁)。

于 2014-09-16T12:18:14.813 回答
3

一个缺失的区别是: std::unique_lock可以移动但std::lock_guard不能移动。

注意:两者都不能复制。

于 2020-09-06T10:02:27.703 回答
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;
}

在这个例子中,我使用了unique_lock<muType>withcondition variable

于 2020-05-28T23:17:12.023 回答
-5

As has been mentioned by others, std::unique_lock tracks the locked status of the mutex, so you can defer locking until after construction of the lock, and unlock before destruction of the lock. std::lock_guard does not permit this.

There seems no reason why the std::condition_variable wait functions should not take a lock_guard as well as a unique_lock, because whenever a wait ends (for whatever reason) the mutex is automatically reacquired so that would not cause any semantic violation. However according to the standard, to use a std::lock_guard with a condition variable you have to use a std::condition_variable_any instead of std::condition_variable.

Edit: deleted "Using the pthreads interface std::condition_variable and std::condition_variable_any should be identical". On looking at gcc's implementation:

  • std::condition_variable::wait(std::unique_lock&) just calls pthread_cond_wait() on the underlying pthread condition variable with respect to the mutex held by unique_lock (and so could equally do the same for lock_guard, but doesn't because the standard doesn't provide for that)
  • std::condition_variable_any can work with any lockable object, including one which is not a mutex lock at all (it could therefore even work with an inter-process semaphore)
于 2013-12-17T23:54:22.653 回答