C++线程数据竞争、互斥对象以及为数据加锁

数据竞争

如下图代码,在进程未执行完的时候,线程就已经开始运行了一段数据,并且输出了错误的结果
在这里插入图片描述
主线程跟子线程都在竞相输出到屏幕上,造成一个数据的竞争,按照国际惯例,咱们先复制运行一段代码加以理解:

#include<iostream>
#include<thread>
#include<stdio.h>
#include<mutex>
//std::mutex mu;
// void shared_print(std::string msg,int id)
// {
//     mu.lock();
//     std::cout << msg << id << std::endl;
//     mu.unlock();
// }
void fuction_1()
{
    for (int i = 0; i > -100; i--)
    {
        //shared_print("线程开始运行",i);
        std::cout<<"线程开始运行" << i <<std::endl;
    }
    
}
int main()
{
    std::thread t1(fuction_1);
    for(int i=0;i<100;i++)
    {
        //shared_print("进程开始运行",i);
        std::cout<<"进程开始运行" << i <<std::endl;
    }
    t1.join();
    std::cout<<"进程准备结束" <<std::endl;
    return 0;
}

这种竞争毫无疑问是有害得的,所以我们可以给数据加锁,首先记得加入mutex头文件,输出结果如下:lock是加锁,unlock是解锁加上锁时别的资源再想访问就必须得等待到锁解开的时候。

数据加锁:

在这里插入图片描述
进程跟线程交替输出原因是:在线程开始运行后,进程输出一条数据,释放锁unlock()之后,线程则会去获取这个锁,取得屏幕输出的控制权,而进程则会等待有机会获取到锁的时候,有控制权了再进行一个输出,代码如下图所示:

#include<iostream>
#include<thread>
#include<stdio.h>
#include<mutex>
std::mutex mu;
void shared_print(std::string msg,int id)
{
    mu.lock();
    std::cout << msg << id << std::endl;
    mu.unlock();
}
void fuction_1()
{
    for (int i = 0; i > -100; i--)
    {
        shared_print("线程开始运行",i);
        //std::cout<<"线程开始运行" << i <<std::endl;
    }
    
}
int main()
{
    std::thread t1(fuction_1);
    for(int i=0;i<100;i++)
    {
        shared_print("进程开始运行",i);
        //std::cout<<"进程开始运行" << i <<std::endl;
    }
    t1.join();
    std::cout<<"进程准备结束" <<std::endl;
    return 0;
}

但是这里还是有一个问题,std::cout << msg << id << std::endl;这一句如果出现异常的话,那将永远不会解锁,此处可以加个try捕获异常,然后抛出异常时解锁(不了解try与catch可以看博主前期的文章),不然程序会可能无法继续运行,还有一种更好的方法,使用lock_guard,lock_guard在控制权离开作用域时会自动销毁锁,lock_guard也有一点不好的地方,他并不是完全地保护资源,别的程序依旧可以跳过他去访问资源,最好的办法就是把资源放进定义的类私有数据成员处,加个函数调用上锁资源,只有使用这个类的函数才能够访问资源,这样资源也处于你时刻的保护之中,详情如下所示:

安全地给数据加锁:

使用lock_guard给数据加锁,类 lock_guard 是互斥体包装器,为在作用域块期间占有互斥提供便利 RAII 风格机制。

创建 lock_guard 对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。

lock_guard 类不可被复制,

#include<iostream>
#include<thread>
#include<stdio.h>
#include<string>
#include<mutex>
#include <fstream>
class LofFile
{
    public:
        LofFile(){
            f.open("log.txt");//构造函数时自动创建/打开log.txt文件,用来存放编译出来的数据
        }
        void shared_print(std::string id,int value)
        {
            std::lock_guard<std::mutex>locker(m_mutex);
            f <<"From"<<id<<":"<< value<<std::endl;
            //离开这个作用域则lock_guard会自动销毁
        }
    protected:
    private:
        std::mutex m_mutex;
        //定义为私有,只有通过调用shared_print函数
        std::ofstream f;
        //资源也定义为私有,想调用此资源就绕不开上面的mutex锁
};
void fuction_1(LofFile& log)//取得log对象,然后才可以调用shared_print
{
    for (int i = 0; i > -100; i--)
    {
        log.shared_print("线程开始运行",i);
    }
    
}
int main()
{
    LofFile log;//初始化一个log对象
    std::thread t1(fuction_1,std::ref(log));
    for(int i=0;i<100;i++)
    {
        log.shared_print("进程开始运行",i);
    }
    t1.join();
    std::cout<<"进程准备结束" <<std::endl;
    return 0;
}

运行结束查看log.txt文件,可以看到数据都有序地输出了
在这里插入图片描述

死锁

死锁:a线程用到1跟2两个锁,然后b线程用到2跟1,顺序不一样,导致a线程获取完1锁后,b线程把2夺取了,所以a在等b释放锁2,b也在等a释放锁1,如何避免死锁:ab线程获取锁的顺序都给他写成一样

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值