【加锁 】

2023-12-14 14:39:19

在这里插入图片描述

定义锁的两种方案
1.定义全局锁
直接在全局用 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2.定义局部锁
pthread_mutex_init
pthread_mutex_t 是库提供的一种数据类型
第二个形参是锁的属性,我们不关心直接Null

我们要把对应的资源保护起来,要保证多个线程申请的是同一把锁

共享变量加上锁保护后就叫临界资源
线程中不是每行代码都在访问临界资源
而是这一小块代码在访问临界资源tickets,我们把这一小块临界资源的代码叫做临界区
在这里插入图片描述
加锁是不是一件好事呢?
根据对互斥的学习,任何时刻只允许一个线程去访问临界资源
加锁的本质其实是对被加锁的代码区域让多线程串行访问

加锁不是一个特别好的事情,
加锁的本质: 是用时间来换安全
加锁的表现: 线程对于临界区代码串行执行
加锁原则: 尽量的要保证临界区代码,越少越好!

在循环外面加锁行不行?
在这里插入图片描述

1.从逻辑上不对,因为你难道想让一个线程在while循环中,一个线程把票全抢完然后才能到下一个线程抢吗,下一个线程抢都没票了。
2.而且临界区限定的区域让代码越少越好,越少串行访问临界资源就越快

所以这样写是不对的


抢票时每个人先申请锁,谁拿到了锁谁才能进入临界区访问抢票
没有拿到锁的线程,就必须要在指定的锁上等待
申请锁成功,才能往后执行,不成功,阻塞等待。
表现形式就是在pthread_mutex_lock函数这里就卡住了
线程申请锁失败了,线程阻塞等待的本质是线程等待锁资源不就绪,调度器把线程PCB阻塞后去锁的队列里进行等待
在这里插入图片描述
但是这里还有问题,可能一个线程抢到锁了,在抢票时发现票没了直接break
此时解锁代码没有被执行,这个线程已经走了,但是锁一直没释放,其他线程会一直阻塞


正确写法
在这里插入图片描述

验证一下发现所有的票几乎被一个线程都抢走了,一会说这个问题。
但确实解决了抢到负数的问题
在这里插入图片描述
但是这样也不明显,我们再加上一句usleep(13)
在这里插入图片描述

在这里插入图片描述
这里面有非常多细节
1.刚刚没有usleep,发现一个锁一直被一个线程抢,正常吗?
在这里插入图片描述
正常,每一个线程抢票时,它一释放锁立马循环回去申请锁
线程对于锁的竞争能力可能会不同
因为你离锁最近,别的线程还没来及唤醒呢,唤醒其他线程的成本比持有锁的线程直接再去抢高,他一释放就去抢其他线程可能还要跑很多代码才能抢到

那为什么加了usleep就可以了呢?
在这里插入图片描述
因为一加usleep 这个锁你释放了先别着急申请锁你先等一等
你usleep期间其他线程就有机会拿到锁然后申请了
说明对于锁的访问一定是并发的,只不过因为一个线程竞争能力太强导致其他人抢不到

这份代码其实不太符合逻辑,我们抢到了票,我们会立马抢下一张吗?
不是
因为多线程抢到之后还有后续的工作比如把票信息插入你的名下
所以就用usleep来模拟后续的代码

纯互斥环境,如果锁分配不够合理,容易导致其他线程的饥饿问题!
但不是说只要有互斥,必有饥饿。适合纯互斥的场景,就用互斥
如果把抢票逻辑写完全了,纯互斥就够了

那该怎么解决饥饿呢?
1.外面来的,必须排队
假如外面有100个线程,如果持有锁线程释放了,如果外面线程不排队那么OS要唤醒
100个线程全去抢锁,但只有一把锁,也就是说99个都是无效唤醒,这不合理
所以要排队

2.出来的人,不能立马重新申请锁,必须排到队列的尾部

让所有的线程(人)获取锁(钥匙),按照一定的顺序。按照一定的顺序性获取资源—同步!!
不一定非得按照队列,爱是什么结构是什么,只要有顺序性

文章来源:https://blog.csdn.net/weixin_50809457/article/details/134991493
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。