您的位置:首页 > 游戏 > 游戏 > 开发app代驾软件多少钱_怎么建网站卖产品_友链外链app_百度云账号登录

开发app代驾软件多少钱_怎么建网站卖产品_友链外链app_百度云账号登录

2024/10/10 2:20:56 来源:https://blog.csdn.net/2201_75663820/article/details/142792077  浏览:    关键词:开发app代驾软件多少钱_怎么建网站卖产品_友链外链app_百度云账号登录
开发app代驾软件多少钱_怎么建网站卖产品_友链外链app_百度云账号登录

目录

一.前置知识

1.为什么要有锁这么一个东西?

2.互斥锁的基本原理是什么?

二.互斥锁

1.锁的库函数接口——原生线程库

a.锁的初始化

b.加锁

c.解锁

d.锁的销毁

e.代码示意

2.互斥锁的底层原理

a.临界区的概念

b.原子性

c.互斥锁的底层原理

3.代码实战

三.线程的同步和环境变量

1.死锁

a.死锁的概念

b.死锁产生的特征

c.避免死锁的方法

2.线程同步

3.条件变量

a.条件变量的底层机理

b.条件变量的函数接口

①条件变量的初始化

②等待条件变量

③唤醒条件变量

④销毁条件变量


一.前置知识

1.为什么要有锁这么一个东西?

在先前的文章中,我们有了解到多线程,我们知道:当同一个进程内,有多个线程同时访问某个公共资源时,就有可能导致得到的数据与预期不一致问题,即线程安全问题!

如:当多线程同时向屏幕打印数据时,屏幕上显示的内容就会十分混乱。因为对线程而言,屏幕也是公共资源,也是线程间竞争的对象。

所以,为避免多线程对公共资源的竞争引发的数据不一致问题,我们需要让多线程有序的访问公共资源,即,确保在同一时刻只有一个线程可以访问共享资源,从而避免数据竞争和不一致性问题。

而实现这种保障机制的手段,就是互斥锁~~

2.互斥锁的基本原理是什么?

互斥锁是如何确保在同一时刻只有一个线程可以访问共享资源的?

举例:当线程A想要访问共享资源时,要先拿到锁,然后对接下来的资源访问步骤进行加锁,当线程A对该共享资源访问完成后再进行解锁操作。在线程A进行资源访问的过程中,若线程B也想要访问这个共享资源,那么线程B就得先拿到锁,但是,由于锁正在被线程A占据,线程B就无法拿到锁,也就无法访问这个共享资源,其它线程亦是如此。

这样就能保证在同一时刻,只有一个线程能够访问共享资源~~

二.互斥锁

1.锁的库函数接口——原生线程库

a.锁的初始化

①pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

②pthread_mutex_t lock;   pthread_mutex_init(&lock, NULL);

pthread_mutex_init()  的第二个参数为属性对象,通常传递NULL使用默认属性

b.加锁

①阻塞式获取锁:pthread_mutex_lock(&lock);

若 lock 锁被其它线程占用,则当前线程会阻塞等待。

②非阻塞获取锁:int ret = pthread_mutex_trylock(&lock);

若锁已经被其他线程持有,则不会阻塞当前线程,而是返回错误码。

加锁的本质:使多线程访问代码的方式从并发式访问变成串行式访问,是用时间来换取安全的!!

c.解锁

pthread_mutex_unlock(&lock);

只有当线程主动进行解锁操作后,其它线程才能拿到这个锁,然后去访问共享资源~~

d.锁的销毁

pthread_mutex_destroy(&lock);

注意解锁和销毁锁的区别:解锁后,其它线程还能用;销毁锁,意味着释放锁资源,这个锁就不能再被使用了~~

e.代码示意

搭建场景:创建多线程,并让它们同时去访问同一个共享资源。

锁的使用:保证在同一时刻只能有一个线程能够进行 tickets-- 操作

2.互斥锁的底层原理

a.临界区的概念

在加锁和解锁之间的代码,我们称为临界区,所访问的资源称为“临界资源”,我们要保证临界区的代码尽可能的少。

为什么临界区越短越好?

临界区越短,线程在临界区的执行时间就越短:①可以减少多线程同时访问临界资源的可能性,降低了竞态条件发生的概率;②可以减少其它线程因等待锁而被阻塞的时间,提高程序的吞吐量。

细节:①线程对锁的竞争能力可能会不同,如:若不在循环的最后加一个线程休眠代码usleep(),那么锁就可能一直被某一线程抢到。②纯互斥环境,如果锁分配不够合理,就容易导致其它线程出现线程饥饿问题。

b.原子性

情景:我们知道,线程在访问共享资源前,需要先拿到锁。那么,如果有多个线程,同时去抢占一个处于空闲状态的锁,此时,这个锁就变成了多线程同时访问的共享资源,这种情况下难道不会出现数据不一致(线程安全)问题吗?

答:不会,因为线程对锁的访问(申请锁和释放锁)具有原子性!!

那么,什么是原子性?

简单来说,一个操作一旦执行,在执行过程中不可被中断,即这个操作要么全部执行成功,要么全部不执行,不会出现中间情况,这样的操作就具有原子性。

对其他线程来讲,一个线程要么处于有锁的状态,要么处于没锁状态,所以,当前线程访问临界资源的过程,对其他线程来讲是原子的!!

c.互斥锁的底层原理

图文解读:若线程A先将eax寄存器数据置零,随后让eax数据与内存中的锁资源做交换,这样线程A就拿到了锁,如果此时线程A被CPU切换下来,锁就会被放到A的上下文中,当CPU再次调度线程B时,线程B先将eax寄存器置0,随后与内存中锁资源作交换,但此时内存中已经没锁了,线程B申请不到锁,就被CPU挂起到锁的阻塞队列中排队等待。当线程A任务执行完毕后,会进行解锁操作,即先将锁资源归还,然后唤醒线程B,让线程B再去执行申请锁的操作。

加锁:调度线程把内存中的锁资源,以一条汇编指令的方式将其交换到CPU寄存器中,即把数据(锁)交换到了线程的硬件上下文!!

线程在访问临界区时,CPU也还是会执行线程间切换,但是,由于该线程在被从CPU上切下时,并未执行解锁操作,所以纵然其它线程被CPU调度,也无法访问临界资源!!

在我们的体系结构里,都会存在swap、exchange这样的汇编语句,它们可以以一条汇编语句的方式,将CPU寄存器内的数据和内存中的数据(锁)作交换,由于只有一条简单的汇编语句,所以加锁的过程是原子的。

3.代码实战

*锁的应用 --- 封装 --- 多线程抢票的代码 + 问题现象 + 锁的使用 + 锁的封装  

源码链接:https://gitee.com/Coder-Li-YuJie/intelligent-packaging-of-locks

三.线程的同步和环境变量

1.死锁

a.死锁的概念

多条执行流和多个锁的情况下,当两个各自都拥有一个锁的线程,同时想要申请对方的锁时,会申请失败,进入阻塞状态,由于这两个线程被阻塞的同时都身怀对方需要的锁,所以,这两个线程就会一直处于阻塞状态,这种状态被称为死锁。

例如:线程A持有x锁,线程B持有y锁。若线程A在没有释放x锁的情况下冒然去申请y锁,由于y锁被线程B所持有,线程A会陷入阻塞等待状态。此时,若线程B再想去申请x锁,由于x锁被线程A所持有,故线程B也会陷入阻塞等待状态。由于进入阻塞状态的线程A和线程B都无法释放自身的锁资源,这就导致两者会一直阻塞,这就是死锁~~

b.死锁产生的特征

① 互斥条件 —— 一个资源只能被一个执行流执行

② 请求与保持条件 —— 执行流因请求资源而阻塞时,对已获得的资源保持不放

③ 不剥夺条件 —— 执行流已获得的资源,在未使用之前,无法被剥夺(可以通过接口打破该条件)

④ 循环等待条件 —— 若干执行流之间形成一种头尾相连的循环等待资源的关系

小结:条件①是前提条件,难以改变,条件②和③则可以通过接口来打破,条件④则需我们写代码时刻意关注!!

c.避免死锁的方法

避免死锁:确保每个线程在获取锁后都能最终释放锁,否则会导致死锁问题。

锁的顺序:如果多个锁需要被多个线程以不同顺序获取,可能会导致死锁。确保所有线程以相同的顺序获取锁。

性能考虑:频繁获取和释放锁可能会影响性能,特别是在高并发场景下。尽量减少锁的持有时间和锁的粒度。

2.线程同步

为了避免出现死锁情况,我们得对申请锁资源的线程做出限制:

①所以等待锁的线程必须按照某种规则进行排队。

②刚解锁的线程,不能立马重新申请锁,必须排队到队列的尾部。

在保证数据在安全的情况下,让线程访问资源具有一定的顺序性,我们将这种行为称作线程同步。

那么,如何让线程访问资源具有一定的顺序性?--- 排队,让所有申请锁的线程都到一个队列中排队等待,当锁处于就绪状态时,先唤醒队首线程,让队首线程先拿到锁,循环往复~~

线程排队这种机制怎么实现??—— 条件变量

3.条件变量

a.条件变量的底层机理

--- 当一个线程互斥的访问某块空间上的共享资源时,若这块空间上的资源还未被生产出来,它就只能等待,这时就需要条件变量的存在。

b.条件变量的函数接口

①条件变量的初始化

静态初始化:

pthread_cond_t  _cond=PTHREAD_COND_INITALIZER;

动态初始化:

pthread_cond_t cond;    pthread_cond_init(&cond, NULL);  

与锁一样,动态初始化时,第二个参数通常传递NULL,表示使用默认属性。

②等待条件变量

等待条件变量时,线程必须持有与条件变量关联的互斥锁。

pthread_cond_wait (pthread_cond_t*  cond, pthread_mutex_t* mutex):无条件等待,并自动释放互斥锁,直到被唤醒并重新获得互斥锁。

pthread_cond_timewait:在指定时间内等待,如果超时则返回错误。

③唤醒条件变量

pthread_cond_signal:唤醒一个等待该条件变量的线程,如果有多个线程等待,则队列中第一个线程。

pthread_cond_broadcast:唤醒所有等待该条件变量的线程。

销毁条件变量

pthread_cond_destroy(&cond);

注意:环境变量不仅能解决“死锁”问题,还能解决“不同线程对锁的竞争能力不同而导致的线程饥饿”问题!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com