【C++多线程】lock_guard<T>类和unique_lock<T>类

lock_guard<T>

  使用的RAII机制,互斥量在lock_guard<T>对象构造时lock,在其析构时unlock。主要是为了解决在手动lock和unlock时忘记unlock的情况。lock_guard类不可复制,因为一个对象只能关联一个mutex。

锁的粒度

  锁的粒度用来描述通过一个锁保护着的数据量大小。一个细粒度锁能够保护较小的数据量,一个粗粒度锁能够保护较多的数据量。选择粒度对于锁来说很重要,为了保护对应的数据,保证锁有能力保护这些数据也很重要。所以一如了更加灵活的unique_lock<T>。

unique_lock<T>

  unique_lock<T> 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用unique_lock<T>可移动,不可复制。unique_lock<T>能够在需要是lock,用完后unlock,当生命周期结束时若此时互斥量没有解锁,也会像lock_guard<T>一样析构解锁。也就是说类unique_lock<T>是类lock_guard<T>的一个超集。unique_lock<T>相比lock_guard<T>更加灵活,但是效率差一些,因为占用更多的内存。以下是cppreference.com对unique_lock<T>的说明

 随时unlock和lock

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <list>
 5 using namespace std;
 6 
 7 class A {
 8 public:
 9     void input()
10     {
11         for (int i = 0; i < 1000; i++)
12         {
13             // lock_guard<mutex> guard(my_mutex);
14             unique_lock<mutex> my_lock(my_mutex);   //构造时自动lock
15             ilst.push_back(i);
16             my_lock.unlock();   //在生命周期结束前提前unlock,为其他线程执行争取时间
17             cout << "加入数据:" << i << endl;
18             my_lock.lock();     //在需要时又重新上锁
19 
20             //do something with my_mutex
21 
22         }  //生命周期结束,自动在析构函数中unlock
23  
24     }
25 
26     void output()
27     {
28         for (int i = 0; i < 1000; i++)
29         {
30             // lock_guard<mutex> guard(my_mutex);
31             unique_lock<mutex> my_lock(my_mutex);
32             if (!ilst.empty())
33             {
34                 cout << "读读读读出数据:" << ilst.front() << endl;
35                 ilst.pop_front();
36             }
37         }
38     }
39  
40 private:
41      list<int> ilst;
42      mutex my_mutex;
43 };
44  
45 int  main()
46 {
47     A a;
48     thread t1(&A::input, &a); 
49     thread t2(&A::output, &a);
50     t1.join();
51     t2.join();
52     return 0;
53
}

所有权转移

  unique_lock<T>类和unique_ptr类一样属于只能移动,不能复制的类型。unique_lock<T>转移所有权的方式有两种:一是使用std::move();二是添加成员函数,返回临时对象。

#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;

class A {
public:
    // 添加成员函数转移所有权
    unique_lock<mutex> mymove()
    {
        return unique_lock<mutex>(my_mutex); 
    }

    void input()
    {
        for (int i = 0; i < 1000; i++)
        {
            unique_lock<mutex> my_lock = mymove();  //
            ilst.push_back(i);
            cout << "加入数据:" << i << endl;
        } 
 
    }

    void output()
    {
        for (int i = 0; i < 1000; i++)
        {
            unique_lock<mutex> my_lock1(my_mutex); //此时my_lock1与my_mutex关联
            unique_lock<mutex> my_lock2(move(my_lock1)); //使用std::move,将my_lock2与my_mutex关联
            if (!ilst.empty())
            {
                cout << "读读读读出数据:" << ilst.front() << endl;
                ilst.pop_front();
            }
        }
    }
 
private:
     list<int> ilst;
     mutex my_mutex;
};
 
int  main()
{
    A a;
    thread t1(&A::input, &a); 
    thread t2(&A::output, &a);
    t1.join();
    t2.join();
    return 0;
}

第二参数   

  unique_lock<T> 有几个第二参数,std::adopt_lock,std::try _to_lock,std::defer_lock

  std::adopt_lock,将一个已经上锁的mutex,关联到unique_lock对象,该对象咋构造时不再对mutex上锁。需要程序员自己保证在构造unique_lock对象之前已经上锁。用法示例unique_lock<std::mutex> my_lock(my_mutex, std::adopt_lock)。

  std::try _to_lock,在构造对象时尝试去上锁,如果没有上锁成功,并不阻塞。用法示例unique_lock<std::mutex> my_lock(my_mutex, std::try_to_lock)

  std::defer_lock,在构造对象时,不上锁。所以前提就是之前没有被上锁

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <list>
 5 using namespace std;
 6 
 7 class A {
 8 public:
 9     void input()
10     {
11         for (int i = 0; i < 1000; i++)
12         {
13             //前提程序员必须先自己上锁
14             my_mutex.lock();  //前提程序员必须先自己上锁
15             unique_lock<mutex> my_lock(my_mutex, adopt_lock);   //说明前面已经上锁,构造时不需要再上锁
16             ilst.push_back(i);
17             cout << "加入数据:" << i << endl;
18         }  //生命周期结束,自动在析构函数中unlock
19  
20     }
21 
22     void output()
23     {
24         for (int i = 0; i < 1000; i++)
25         {
26             // 前提是前面没有对my_mutex上锁
27             unique_lock<mutex> my_lock(my_mutex, defer_lock); //初始化一个没有上锁的对象,后面使用是自己上锁
28             if (!ilst.empty())
29             {
30                 cout << "读读读读出数据:" << ilst.front() << endl;
31                 ilst.pop_front();
32             }
33         }
34     }
35  
36 private:
37      list<int> ilst;
38      mutex my_mutex;
39 };
40  
41 int  main()
42 {
43     A a;
44     thread t1(&A::input, &a); 
45     thread t2(&A::output, &a);
46     t1.join();
47     t2.join();
48     return 0;
49 }
原文地址:https://www.cnblogs.com/chen-cs/p/13062647.html