C++并发编程之mutex

何时需要锁

一个函数能否被两个线程同时调用?

函数本身只是代码,代码是只读的,无论多少个线程同时调都无所谓。但是函数里面总要用到数据,如果数据属于线程(比如函数参数、局部变量,存在栈上,每个线程都有自己的栈),那么同时调还是没关系,因为是本线程的数据;但是如果用了一些全局数据,比如全局变量,同时操作一个数据结构,那就不行了,这时候这需要用到锁机制。

<mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。

std::mutex

C++ 11提供了4中不同的锁,这里只介绍std::mutex。std::mutex只有几个成员函数,我们这里只需关注lock和unlock函数即可。

std::lock_guard

std::mutex的加锁和解锁相当简单,无需多言。但有一个bug:如果第一个的线程加锁后抛出异常,其申请的锁将无法得到解锁,其它线程就永远无法申请到锁。

std::lock_guard与mutex配合使用,把锁放到lock_guard中时,mutex自动上锁,lock_guard析构时,同时把mutex解锁。

#include <thread> 
#include <mutex>   
int g_i = 0; 
std::mutex g_i_mutex;  
void safe_increment() 
{     
std::lock_guard<std::mutex> lock(g_i_mutex);     
++g_i;       
// g_i_mutex is automatically released when lock     
// goes out of scope 
}   
 
int main() 
{     
std::thread t1(safe_increment);     
std::thread t2(safe_increment);       
t1.join();     
t2.join(); 
}

std::lock_guard是一个局部变量,创建时,g_i_mutex 上锁,析构时g_i_mutex解锁。
这个功能在函数体比较长,尤其是存在多个分支的时候很有用。

std::unique_lock

std::lock_guard完美展现了RAII,但std::lock_guard限制得太死了,只有构造和析构函数,没法通过它的成员函数加锁和解锁。为此,C++11提供了灵活的std:unique_lock模板类。std::unique_lock提供lock和unlock函数,因此可以在适当的时候加解锁。这样可以降低锁的粒度。

默认情况下,std::unique_lock的构造函数会对mutex进行加锁,在析构的时候会对mutex进行解锁。为了避免重复解锁,std::unique_lock内部会记录mutex的状态,在析构函数中根据mutex的状态决定是否需要解锁。

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

std::mutex g_mutex;
void threadFunc(size_t num)
{
    std::unique_lock<std::mutex> ul(g_mutex);

	//do something 

    ul.unlock();//解锁,降低锁的粒度

	//do something 
	
    ul.lock();
    
    std::cout << "thread id " << std::this_thread::get_id() << std::endl;
    
    //ul的析构函数会解锁
}
int main()
{
    std::thread th1(threadFunc, 100);
    std::thread th2(threadFunc, 200);
    th1.join();
    th2.join();
    return 0;
}

std::lock

C++11提供了一个模板函数std::lock()使得很容易原子地对多个锁进行加锁。

template <class Mutex1, class Mutex2, class... Mutexes>
void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

std::lock函数只要求参数有lock操作即可,也就是说可以传一个std::mutex或者std::unique_lock变量给std::lock。std::lock_guard变量则不行,因为其没有lock()函数。

class BigData
{
public:

    void swap(BigData &bg)
    {
        if (this == &bg)
            return;

        std::unique_lock<std::mutex> ul1(m_mutex, std::defer_lock);
        std::unique_lock<std::mutex> ul2(bg.m_mutex, std::defer_lock);

        std::lock(ul1, ul2);
        m_vec.swap(bg.m_vec);
    }

private:
    std::mutex m_mutex;
    std::vector<int> m_vec;
};

std::defer_lock是告诉std::unique_lock的构造函数,在构造函数不要给mutex上锁.
除了std::defer_lock,还可以用std::adopt_lock作为第二个参数。std::adopt_lock和std::defer_lock相反,它表示已经上锁了,不需要再加锁了。std::lock_guard也是支持std::adopt_lock的,但不支持std::defer_lock.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值