Skip to content

c++线程安全

涉及到的头包括

<mutex> <shared_mutex>

互斥量

std::mutex

c++中最基本的互斥量

cpp
#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();
}
#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

可递归的互斥量

cpp
#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();
}
#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

带有超时功能的互斥量

cpp
#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.
    }
}
#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

cpp
#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;
}
#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

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

cpp
#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;
}
#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()方法来手动的控制加锁与解锁,如果程序员忘记解锁了,在出作用的时候独占锁也会自动的解锁

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

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

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

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

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

读写锁例子:

cpp
#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;
}
#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的例子:

cpp
#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.
    }
}
#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起引入范围锁,可以一次同时对多个不同的互斥量进行加锁,进一步防止死锁的发生

cpp
#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;
}
#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标志量来实现

线程安全单例:

cpp
// container.h
class Container {
public:
    static Container *GetInstance();

private:
    Container() = default;
    ~Container() = default;

    static inline Container *instance_ = nullptr;
};
// container.h
class Container {
public:
    static Container *GetInstance();

private:
    Container() = default;
    ~Container() = default;

    static inline Container *instance_ = nullptr;
};
cpp
// container.cc

static std::once_flag instanceFlag;

Container *Container::GetInstance() {
    std::call_once(instanceFlag, [] { instance_ = new Container(); });
    return instance_;
}
// container.cc

static std::once_flag instanceFlag;

Container *Container::GetInstance() {
    std::call_once(instanceFlag, [] { instance_ = new Container(); });
    return instance_;
}