# lock_guard,unique_lock和shared_lock

# lock_guard

  • std::lock_guard 是一个轻量级的互斥锁封装,基于 std::mutex 实现。
  • 基于C++RAII (Resource Acquisition Is Initialization)实现,在构造时加锁,在析构时解锁
  • 适用于简单的互斥操作,例如在有限的作用域内加锁和解锁。
  • 不能手动解锁,只能在作用域结束时自动解锁。

# unique_lock

  • std::unique_lock 是一个更灵活的互斥锁封装,也基于 std::mutex 实现。
  • 可以在构造时选择是否立即加锁,也可以在任意时刻手动解锁。
  • 可以传递不同作用域中互斥量的所有权。
  • 支持更多高级功能,例如超时、条件变量等。
  • 析构时会根据当前锁的状态自动决定是否解锁。

# std::shared_lock

  • std::shared_lockc++17中引入的共享锁
  • std::shared_lock 是一个更灵活的互斥锁封装,也基于 std::mutex 实现,专为共享互斥锁而设计,支持多个读取器。
  • 可以在构造时选择是否立即加锁,也可以在任意时刻手动解锁
  • 适用于共享互斥锁的场景,允许多个线程同时持有共享锁,以读取相同的数据。
  • std::lock_guard 类似,它不支持手动解锁或重新锁定
  • 如果有线程需要写入数据,它必须使用独占锁(std::unique_lock)来获取写入权限。

# 使用场景

  • 只需要简单的加锁和解锁,且作用域较小,可以使用 std::lock_guard
  • 需要设置手动解锁、超时等,在不同作用域传递锁的所有权,应该选择 std::unique_lock
  • 多个线程读取同份数据,少量线程修改数据时使用shared_lock

# 实例

#include <thread>
#include <mutex>
#include <shared_mutex>
#include <iostream>

std::mutex m1;

struct DataBuffer
{
    int x;
    int y;
};

DataBuffer db;

void func1()
{
    m1.lock();
    db.x = 100;
    std::cout << "t1: " << db.x << std::endl;
    m1.unlock();
}

void func2()
{
    m1.lock();
    db.x = 200;
    std::cout << "t2: " << db.x << std::endl;
    m1.unlock();
}

void func3()
{
    std::lock_guard<std::mutex> lock(m1);
    db.x = 100;
    std::cout << "t3: " << db.x << std::endl;
}

void func4()
{
    std::lock_guard<std::mutex> lock(m1);
    db.x = 200;
    std::cout << "t4: " << db.x << std::endl;
}

void func5()
{
    std::unique_lock<std::mutex> lock(m1);
    db.x = 500;
    std::cout << "t50: " << db.x << std::endl;
    lock.unlock();
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    lock.lock();
    db.x = 1000;
    std::cout << "t5: " << db.x << std::endl;
}

void func6()
{
    std::unique_lock<std::mutex> lock(m1);
    db.x = 600;
    std::cout << "t60: " << db.x << std::endl;
    lock.unlock();
    lock.lock();
    db.x = 1200;
    std::cout << "t6: " << db.x << std::endl;
}

class Counter {
    public:
    
    Counter():value_(0) {}

    void increment() { 
        std::unique_lock<std::shared_mutex> lock(m_mutex);
        ++value_;
    }

    int getValue() const { 
        std::unique_lock<std::shared_mutex> lock(m_mutex);
        return value_; 
    }

    private:

    mutable std::shared_mutex m_mutex;
    int value_;
};

int main(int argc, char* argv[])
{
    std::thread t1(func1);
    std::thread t2(func2);
    std::thread t3(func3);
    std::thread t4(func4);
    std::thread t5(func5);
    std::thread t6(func6);

    Counter counter;
    auto f7 = [&counter]() {
        std::cout << "Reader 1: Value = " << counter.getValue() << std::endl;
    };
    std::thread reader1(f7);
    auto f8 = [&counter]() {
        std::cout << "Reader 2: Value = " << counter.getValue() << std::endl;
    };
    std::thread reader2(f8);
    auto f9 = [&counter]() {
        counter.increment();
    }; 
    std::thread writer(f9);

    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    t6.join();
    reader1.join();
    reader2.join();
    writer.join();
    return 0;
}

# reference

1.https://en.cppreference.com/w/cpp/thread/lock_guard (opens new window)