为什么需要锁

在阅读本文前,你需要一点c++知识和一点线程知识

为什么需要锁

我们考虑一个这样的场景,有两个线程都需要修改同一个全局变量,都对他进行n次加一的操作。

#include<pthread.h>
#include<iostream>

using namespace std;

int x=0;
int n=100;
void *pthread_child(void *arg){
    for(int i=0;i<n;i++)//对x加100次
    x++;
    return nullptr;
}

int main(){
    pthread_t p1,p2;
    pthread_create(&p1, NULL, pthread_child, NULL);
    pthread_create(&p2, NULL, pthread_child, NULL);
    pthread_join(p1,NULL);
    pthread_join(p2,NULL);
    cout<<"x="<<x<<endl;
}

编译运行完结果是

x=200

当次数还是100次的时候,一切还很正常,我们尝试着把这个数字变大,比如一万次或者十万次。你可以自己尝试一下。
当我修改成一万次之后,x的值是两万,目前还是很正常。可是当我们把次数修改成十万次之后,奇怪的事情发生了(如果奇怪的事情没有发生,可以再把数字加大),x的值仿佛不受控制了,每次运行的值都不是二十万,而且还不是一个确定的值。

这是为什么呢?

让我们先进入底层看看发生了什么:

线程1运行,对x进行一次加一操作。
在底层,
第一步:线程1需要将内存里面x的值复制到自己的eax寄存器。
第二步:对eax寄存器里面的值进行加一操作。
第三步:再把eax寄存器里面的值复制回内存里面的x

mov 0x8049a1c, %eax
add $0x1, %eax
mov %eax, 0x8049a1c

当线程1开开心心地执行到第二步之后(线程1的eax是1),意外的事情发生了,时钟中断,线程2开始运行,线程2也把x加载到自己的eax寄存器,然后执行第二步,此时的线程2的eax也是1,第三步,把1存回了x。此时线程1终于杀了回来,继续上一次运行,执行第三步,也把1存回了x。

两个线程都执行完了一次x++。但是x只增加了1。

== 细心的小伙伴可能要问了,eax寄存器不是只有一个吗,为什么线程1和线程2一人一个呢。这因为操作系统虚拟化了寄存器,简单来说,就是每一个线程有一组数据,这组数据包含各个寄存器的值,在操作系统切换线程时,把当前各个寄存器的值保存到线程这组数据中,然后再把下一个线程的数据加载到寄存器中 ==

如何解决呢

yes!!!此时就需要我们的锁子哥上场了,锁就是保证了临界区操作的“原子性”。为什么加引号呢,因为锁只保证在同一个锁下的原子性。

我们给上述代码加上锁之后就不会出现

如下:

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

void *pthread_child(void *arg){
    
    for(int i=0;i<n;i++)
    {
    pthread_mutex_lock(&m);
    x++;
    pthread_mutex_unlock(&m);
    }
    return nullptr;
}

这个锁的位置我是随便加的,可能性能不好,但是大家可以根据自己的程序选定一个临界区

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zxx147

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值