c++互斥锁

分类: c++

2021-02-24

|

83

|

评论:0

分享:

什么是互斥量?


这样比喻:单位上有一台打印机(共享数据a),你要用打印机(线程1要操作数据a),同事老王也要用打印机(线程2也要操作数据a),但是打印机同一时间只能给一个人用,此时,规定不管是谁,在用打印机之前都要向领导申请许可证(lock),用完后再向领导归还许可证(unlock),许可证总共只有一个,没有许可证的人就等着在用打印机的同事用完后才能申请许可证(阻塞,线程1lock互斥量后其他线程就无法lock,只能等线程1unlock后,其他线程才能lock)。那么,打印机就是共享数据,访问打印机的这段代码就是临界区,这个必须互斥使用的许可证就是互斥量

互斥量是为了解决数据共享过程中可能存在的访问冲突的问题。这里的互斥量保证了使用打印机这一过程不被打断。

互斥量怎么使用?

首先需要#include<mutex>;(std::mutex和std::lock_guard都在<mutex>头文件中声明。)

然后需要实例化std::mutex对象;

最后需要在进入临界区之前对互斥量加锁,退出临界区时对互斥量解锁;

至此,互斥量走完了它的一生。


lock()与unlock():

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;//实例化m对象,不要理解为定义变量
void proc1(int a)
{
    m.lock();
    cout << "proc1函数正在改写a" << endl;
    cout << "原始a为" << a << endl;
    cout << "现在a为" << a + 2 << endl;
    m.unlock();
}

void proc2(int a)
{
    m.lock();
    cout << "proc2函数正在改写a" << endl;
    cout << "原始a为" << a << endl;
    cout << "现在a为" << a + 1 << endl;
    m.unlock();
}
int main()
{
    int a = 0;
    thread proc1(proc1, a);
    thread proc2(proc2, a);
    proc1.join();
    proc2.join();
    return 0;
}

不推荐实直接去调用成员函数lock(),因为如果忘记unlock(),将导致锁无法释放,使用lock_guard或者unique_lock则能避免忘记解锁带来的问题。

lock_guard:

td::lock_guard()是什么呢?它就像一个保姆,职责就是帮你管理互斥量,就好像小孩要玩玩具时候,保姆就帮忙把玩具找出来,孩子不玩了,保姆就把玩具收纳好。

其原理是:声明一个局部的std::lock_guard对象,在其构造函数中进行加锁,在其析构函数中进行解锁。最终的结果就是:创建即加锁,作用域结束自动解锁。从而使用std::lock_guard()就可以替代lock()与unlock()。

通过设定作用域,使得std::lock_guard在合适的地方被析构(在互斥量锁定到互斥量解锁之间的代码叫做临界区(需要互斥访问共享资源的那段代码称为临界区),临界区范围应该尽可能的小,即lock互斥量后应该尽早unlock),通过使用{}来调整作用域范围,可使得互斥量m在合适的地方被解锁

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;//实例化m对象,不要理解为定义变量
void proc1(int a)
{
    lock_guard<mutex> g1(m);//用此语句替换了m.lock();lock_guard传入一个参数时,该参数为互斥量,此时调用了lock_guard的构造函数,申请锁定m
    cout << "proc1函数正在改写a" << endl;
    cout << "原始a为" << a << endl;
    cout << "现在a为" << a + 2 << endl;
}//此时不需要写m.unlock(),g1出了作用域被释放,自动调用析构函数,于是m被解锁

void proc2(int a)
{
    {
        lock_guard<mutex> g2(m);
        cout << "proc2函数正在改写a" << endl;
        cout << "原始a为" << a << endl;
        cout << "现在a为" << a + 1 << endl;
    }//通过使用{}来调整作用域范围,可使得m在合适的地方被解锁
    cout << "作用域外的内容3" << endl;
    cout << "作用域外的内容4" << endl;
    cout << "作用域外的内容5" << endl;
}
int main()
{
    int a = 0;
    thread proc1(proc1, a);
    thread proc2(proc2, a);
    proc1.join();
    proc2.join();
    return 0;
}

std::lock_gurad也可以传入两个参数,第一个参数为adopt_lock标识时,表示构造函数中不再进行互斥量锁定,因此此时需要提前手动锁定

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;//实例化m对象,不要理解为定义变量
void proc1(int a)
{
    m.lock();//手动锁定
    lock_guard<mutex> g1(m,adopt_lock);
    cout << "proc1函数正在改写a" << endl;
    cout << "原始a为" << a << endl;
    cout << "现在a为" << a + 2 << endl;
}//自动解锁

void proc2(int a)
{
    lock_guard<mutex> g2(m);//自动锁定
    cout << "proc2函数正在改写a" << endl;
    cout << "原始a为" << a << endl;
    cout << "现在a为" << a + 1 << endl;
}//自动解锁
int main()
{
    int a = 0;
    thread proc1(proc1, a);
    thread proc2(proc2, a);
    proc1.join();
    proc2.join();
    return 0;
}

unique_lock:

std::unique_lock类似于lock_guard,只是std::unique_lock用法更加丰富,同时支持std::lock_guard()的原有功能。 使用std::lock_guard后不能手动lock()与手动unlock();使用std::unique_lock后可以手动lock()与手动unlock(); std::unique_lock的第二个参数,除了可以是adopt_lock,还可以是try_to_lock与defer_lock;

try_to_lock: 尝试去锁定,得保证锁处于unlock的状态,然后尝试现在能不能获得锁;尝试用mutx的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里,并继续往下执行;

defer_lock: 始化了一个没有加锁的mutex;

#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex m;
void proc1(int a)
{
    unique_lock<mutex> g1(m, defer_lock);//始化了一个没有加锁的mutex
    cout << "xxxxxxxx" << endl;
    g1.lock();//手动加锁,注意,不是m.lock();注意,不是m.lock(),m已经被g1接管了;
    cout << "proc1函数正在改写a" << endl;
    cout << "原始a为" << a << endl;
    cout << "现在a为" << a + 2 << endl;
    g1.unlock();//临时解锁
    cout << "xxxxx"  << endl;
    g1.lock();
    cout << "xxxxxx" << endl;
}//自动解锁

void proc2(int a)
{
    unique_lock<mutex> g2(m,try_to_lock);//尝试加锁一次,但如果没有锁定成功,会立即返回,不会阻塞在那里,且不会再次尝试锁操作。
    if(g2.owns_lock){//锁成功
        cout << "proc2函数正在改写a" << endl;
        cout << "原始a为" << a << endl;
        cout << "现在a为" << a + 1 << endl;
    }else{//锁失败则执行这段语句
        cout <<""<<endl;
    }
}//自动解锁

int main()
{
    int a = 0;
    thread proc1(proc1, a);
    thread proc2(proc2, a);
    proc1.join();
    proc2.join();
    return 0;
}

使用try_to_lock要小心,因为try_to_lock尝试锁失败后不会阻塞线程,而是继续往下执行程序,因此,需要使用if-else语句来判断是否锁成功,只有锁成功后才能去执行互斥代码段。而且需要注意的是,因为try_to_lock尝试锁失败后代码继续往下执行了,因此该语句不会再次去尝试锁。


Powered by Froala Editor



转载请注明来源

文章:c++互斥锁

链接:/article/34

作者:大猫

标签: 多线程 同步锁
本文共 0 个回复

发表评论 (对文章评论)

captcha