• 博客
  • 音乐
  • 随想
  • 足迹
  • 关于
  • 个人空间
    • 登录
animals
    bg
    c++线程安全
    所属分类:c++
    发布时间:2025-05-19 10:03

    c++线程安全

    涉及到的头包括

    <mutex> <shared_mutex>

    互斥量

    std::mutex

    c++中最基本的互斥量

    #include <mutex>
    #include <vector>
    
    std::mutex mutex;
    std::vector<int> vector;
    
    void Set(int value) {
        mutex.lock();
        vector.push_back(value);
        mutex.unlock();
    }
    
    std::vector<int> Get() {
        mutex.lock();
        std::vector<int> result = vector;
        mutex.unlock();
    }
    

    std::recursive_mutex

    可递归的互斥量

    #include <mutex>
    #include <vector>
    
    std::recursive_mutex rmutex;
    std::vector<int> vector;
    
    bool Exist(int value) {
        rmutex.lock()
        for (int i : vector) {
            if (i == value) {
                rmutex.unlock();
                return true;
            }
        }
        rmutex.unlock();
        return false;
    }
    
    void Set(int value) {
        rmutex.lock();
        if (Exist(value)) {
            rmutex.unlock();
            return;
        }
        vector.push_back(value);
        rmutex.unlock();
    }
    
    std::vector<int> Get() {
        rmutex.lock();
        std::vector<int> result = vector;
        rmutex.unlock();
    }
    

    std::time_mutex 与 std::recursive_timed_mutex

    带有超时功能的互斥量

    #include <chrono>
    #include <mutex>
    #include <vector>
    
    using namespace std::chrono_literals;
    
    std::vector<int> vector;
    std::timed_mutex tmutex;
    
    void Set() {
        if (tmutex.try_lock_for(3s)) {
            // can lock, set something.
            tmutex.unlock();
        } else {
            // can't lock, do nothing.
        }
    }
    

    std::shared_mutex 与std::shared_timed_mutex

    从c++17起引入了std::shared_mutex,c++17之前想使用共享互斥量可以使用std::shared_timed_mutex

    #include <shared_mutex>
    #include <vector>
    
    #if __cplusplus < 201703L
    std::shared_timed_mutex mutex;
    #else
    std::shared_mutex mutex;
    #endif
    
    std::vector<int> vector;
    
    void Set(int value) {
        mutex.lock(); // 独占上锁
        vector.push_back(value);
        mutex.unlock();
    
    }
    
    std::vector<int> Get() {
        mutex.lock_shared(); // 共享上锁
        std::vector<int> result = vector;
        mutex.unlock_shared();
        return result;
    }
    

    锁

    手动操控互斥量很烦锁,而且很容易忘记解锁,所以标准库引入了”工具锁“来帮助程序员更方便的控制互斥量

    std::lock_guard

    守护锁,实现最为简单的锁,是一个“不可控”的锁,也就是说一旦锁上了,只能等待出作用域守护锁才能自动解锁,是性能最好的锁

    #include <mutex>
    #include <vector>
    
    std::mutex mutex;
    std::vector<int> vector;
    
    void Set(int value) {
    #if __cplusplus < 201703L
        std::lock_guard<std::mutex> guard(mutex);
    #else
        std::lock_guard guard(mutex);
    #endif
        vector.push_back(value);
    }
    
    std::vector<int> Get() {
    #if __cplusplus < 201703L
        std::lock_guard<std::mutex> guard(mutex);
    #else
        std::lock_guard guard(mutex);
    #endif
        return vector;
    }
    
    

    std::unique_lock与std::shared_lock

    独占锁与共享锁

    • 与守护锁相比,独占锁是一个灵活的锁,但是与守护锁相比性能略差,可以通过lock()与unlock()方法来手动的控制加锁与解锁,如果程序员忘记解锁了,在出作用的时候独占锁也会自动的解锁

    • 独占锁与共享锁在读写的场景下使用较多,也就是俗称的”读写锁“

    • 独占锁与共享锁的特性:

      在独占锁加锁时,在解锁前,所有对该互斥量请求加锁的线程都会被阻塞

      在共享锁加锁时,如果其他线程对该互斥量请求共享加锁不会阻塞,而对该互斥量请求独占加锁将会阻塞

      在共享锁加锁时,如果既有独占锁请求加锁也有共享锁请求加锁,那么独占锁请求后将会阻塞之后的共享锁的请求

    读写锁例子:

    #include <shared_mutex>
    #include <vector>
    
    #if __cplusplus < 201703L
    std::shared_timed_mutex mutex;
    #else
    std::shared_mutex mutex;
    #endif
    std::vector<int> vector;
    
    void Set(int value) {
    #if __cplusplus < 201703L
        std::unique_lock<std::shared_timed_mutex> lock(mutex);
    #else
        std::unique_lock lock(mutex);
    #endif
        vector.push_back(value);
    }
    
    std::vector<int> Get() {
    #if __cplusplus < 201703L
        std::shared_lock<std::shared_timed_mutex> lock(mutex);
    #eles
        std::shared_lock lock(mutex);
    #endif
        return vector;
    }
    

    unique_lock的例子:

    #include <chrono>
    #include <mutex>
    #include <vector>
    
    using namespace std::chrono_literals;
    
    std::mutex mutex;
    std::timed_mutex tmutex;
    std::vector<int> vector;
    
    void Set(int value) {
        std::unique_lock lock(mutex);
        vector.push_back(value);
        lock.unlock();
    
        // do something
    
        lock.lock();
        vector.push_back(value * value);
        lock.unlock();
    }
    
    void SetWithTime(int value) {
        std::unique_lock lock(tmutex, std::defer_lock_t); // 延迟加锁
        if (lock.try_lock_for(3s)) {
            // can lock, set something.
            lock.unlock();
        } else {
            // can't lock, do nothing.
        }
    }
    

    std::scoped_lock

    从c++17起引入范围锁,可以一次同时对多个不同的互斥量进行加锁,进一步防止死锁的发生

    #include <mutex>
    
    std::vector<int> a;
    std::vector<int> b;
    
    std::mutex amutex;
    std::mutex bmutex;
    
    void Set(int i, int j) {
        std::scoped_lock lock(amutex, bmutex);
        a.push_back(i);
        b.push_back(j);
    }
    
    std::vector<int> GetA() {
        std::lock_guard guard(amutex);
        return a;
    }
    
    std::vector<int> GetB() {
        std::lock_guard guard(bmutex);
        return b;
    }
    
    

    std::once_flag 与 std::call_once

    在多线程中,有一些场景是某个任务只需要执行一次,可以用std::call_once函数配合std::once_flag标志量来实现

    线程安全单例:

    // container.h
    class Container {
    public:
        static Container *GetInstance();
    
    private:
        Container() = default;
        ~Container() = default;
    
        static inline Container *instance_ = nullptr;
    };
    
    // container.cc
    
    static std::once_flag instanceFlag;
    
    Container *Container::GetInstance() {
        std::call_once(instanceFlag, [] { instance_ = new Container(); });
        return instance_;
    }