C++ 并发编程,std::unique_lock与std::lock_guard 等各种stl里面的东西

std::mutex

mutex 类是能用于保护共享数据免受从多个线程同时访问的同步原语。mutex 提供排他性非递归所有权语义。

操作:
1.lock:如果 mutex 未上锁,则将其上锁。否则如果已经其它线程 lock,则阻塞当前线程
2.try_lock:如果 mutex 未上锁,则将其上锁。否则返回 false,并不阻塞当前线程
3.unlock:如果 mutex 被当前线程锁住,则将其解锁。否则,是未定义的行为
4.native_handle:返回底层实现定义的线程句柄

例子:

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

int g_num = 0;//为 g_num_mutex 所保护
std::mutex g_num_mutex;

void slow_increment(int id) {
    for(int i = 0; i < 3; ++i) {
        g_num_mutex.lock();
        ++g_num;
        cout << id << " => " << g_num << endl;
        g_num_mutex.unlock();

        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main(void) {
    std::thread t1(slow_increment, 0);
    std::thread t2(slow_increment, 1);
    t1.join();
    t2.join();

// 输出:
// 0 => 1
// 1 => 2
// 0 => 3
// 1 => 4
// 0 => 5
// 1 => 6

    return 0;
}

std::lock_guard

std::lock_guard是RAII模板类的简单实现,功能简单。
1.std::lock_guard 在构造函数中进行加锁,析构函数中进行解锁。
2.锁在多线程编程中,使用较多,因此c++11提供了lock_guard模板类;在实际编程中,我们也可以根据自己的场景编写resource_guard RAII类,避免忘掉释放资源。

下面是一个使用std::lock_guard的代码例子,1+2+ … + 100的多线程实现,每个num只能由一个线程处理:

#include <thread>
#include <mutex>
#include <vector>
#include <iostream>
#include <algorithm>

std::mutex my_lock;

void add(int &num, int &sum){
    while(true){
        std::lock_guard<std::mutex> lock(my_lock);  
        if (num < 100){ //运行条件
            num += 1;
            sum += num;
        }   
        else {  //退出条件
            break;
        }   
    }   
}

int main(){
    int sum = 0;
    int num = 0;
    std::vector<std::thread> ver;   //保存线程的vector
    for(int i = 0; i < 20; ++i){
        std::thread t = std::thread(add, std::ref(num), std::ref(sum));
        ver.emplace_back(std::move(t)); //保存线程
    }   

    std::for_each(ver.begin(), ver.end(), std::mem_fn(&std::thread::join)); //join
    std::cout << sum << std::endl;
}

std::unique_lock

类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
unique_lock比lock_guard使用更加灵活,功能更加强大。
使用unique_lock需要付出更多的时间、性能成本。

下面是try_lock的使用例子。

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock
#include <vector>

std::mutex mtx;           // mutex for critical section
std::once_flag flag;

void print_block (int n, char c) {
    //unique_lock有多组构造函数, 这里std::defer_lock不设置锁状态
    std::unique_lock<std::mutex> my_lock (mtx, std::defer_lock);
    //尝试加锁, 如果加锁成功则执行
    //(适合定时执行一个job的场景, 一个线程执行就可以, 可以用更新时间戳辅助)
    if(my_lock.try_lock()){
        for (int i=0; i<n; ++i)
            std::cout << c;
        std::cout << '\n';
    }
}

void run_one(int &n){
    std::call_once(flag, [&n]{n=n+1;}); //只执行一次, 适合延迟加载; 多线程static变量情况
}

int main () {
    std::vector<std::thread> ver;
    int num = 0;
    for (auto i = 0; i < 10; ++i){
        ver.emplace_back(print_block,50,'*');
        ver.emplace_back(run_one, std::ref(num));
    }

    for (auto &t : ver){
        t.join();
    }
    std::cout << num << std::endl;
    return 0;
}

死锁

std::mutex 的成员函数
构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:
(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。



#include <iostream>
#include <mutex>

class myTestMutex
{
public:
	myTestMutex(int m_m) : m(m_m) {}

	~myTestMutex() {}

	void Add(int number)
	{
		m_mutex.lock();
		m += number;
		m_mutex.unlock();
	}

	void PlusOne()
	{
		m_mutex.lock();
		Add(1);
		m_mutex.unlock();
	}

private:
	std::mutex m_mutex;
	int m;
};
int main()
{
	myTestMutex test(0);
	//调用Add方法是没有问题的,而且有原子操作的效果。
	test.Add(10);
	//但是一定调用PlusOne方法就出现问题了,因为进入PlusOne方法的时候会调用m_mutex.lock()方法,
	//还没有调用m_mutex.unlock()方法就马上进入Add方法了,然后Add方法马上又调用了m_mutex.lock(),
	//然后m_mutex.lock()因为前面在PlusOne方法调用m_mutex.lock()方法,所以就出现阻塞,然后在Add方法就走不下去了,
	//然后因为PlusOne方法调用了Add方法,所以导致PlusOne方法也走不下去了,出现死锁了。
	test.PlusOne();
}

std::condition_variable

这篇博客讲的很好
https://www.jianshu.com/p/c1dfa1d40f53

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值