龙空技术网

多线程——线程安全

马士兵教育 322

前言:

目前各位老铁们对“线程安全和非线程安全的区别”大致比较关切,看官们都想要知道一些“线程安全和非线程安全的区别”的相关知识。那么小编同时在网络上汇集了一些对于“线程安全和非线程安全的区别””的相关知识,希望大家能喜欢,兄弟们一起来了解一下吧!

文章目录前言线程安全互斥的实现:互斥锁死锁同步的实现----条件变量前言

上一个博客总结了线程控制:创建/终止/等待/分离。需要了解请点击:链接: 线程控制.

线程安全

线程安全:多个执行流对临界资源的争抢访问,但是不会出现数据二义性;

线程安全的实现:

同步:通过条件判断保证对临界资源访问的合理性

互斥:通过同一时间对临界资源访问的唯一性实现临界资源访问的安全性

互斥的实现:互斥锁

互斥锁实现的原理:互斥锁本身是一个只有0/1的计数器,描述了一个临界资源当前的访问状态,所有执行流在访问临界资源,都需要先判断当前的临界资源状态是否允许访问,如果不允许则让执行流等待,否则可以让执行流访问临界资源,但是在访问期间需要将状态修改为不可访问状态,这期间如果有其他执行流想要访问,则不被允许。

互斥锁具体的操作流程以及接口介绍:

定义互斥锁变量 pthread_mutex_t mutex;pthread_mutex_init(pthread_mutex_t mutex,pthread_mutexattr_t attr);pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;定义并且初始化在访问临界资源之前进行加锁操作(不能加锁则等待,可以加锁则修改资源状态,然后调用返回,访问临界资源)

pthread_mutex_lock(pthread_mutex_t mutex);

pthread_mutex_trylock(pthread_mutex_t *mutex);(非阻塞:不能加锁立即报错返回)

挂起等待:将线程状态置为可中断休眠状态–表示当前休眠;

被唤醒:将线程状态置为运行状态在临界资源访问完毕之后进行解锁操作(将资源状态置为可访问,将其他执行流唤醒)

pthread_mutex_unlock(pthread_mutex_t mutex);销毁互斥锁

pthread_mutex_destroy(pthread_mutex_t mutex)

所有的执行流都需要通过同一个互斥锁实现互斥,意味着互斥锁本身就是一个临界资源,大家都会访问

如果互斥锁本身的操作都不安全如何保证别人安全,所以互斥锁本身的操作首先必须是安全的------互斥锁自身计数的操作是原子操作

我们可以发现互斥锁,我们实现代码中有四个线程,但是模拟抢票的时候,只有一个线程在抢,所以互斥并不保证合理。

死锁

多个执行流对锁资源进行争抢访问,但是因为访问推进顺序不当,造成互相等到最终导致程序流程无法继续推进,这时候就造成了死锁。

死锁实际是一种程序流程无法继续推进,卡在某个位置的一种概念。

死锁产生的必要条件:

1.互斥条件

2.不可剥夺条件:我加的锁,别人不能解,只有我能解锁

3.请求与保持条件:我加了A锁,然后去请求B锁;如果不能对B加锁,则也不释放A锁

4.环路等待条件:我加了A锁,然后去请求B锁,另一个人加了B锁,然后去请求A锁。

死锁的预防:破坏死锁产生的必要条件(主要避免3和4两个条件的产生)

死锁的避免:银行家算法

互斥并不保证合理/同步并不保证安全

同步的实现----条件变量

同步的实现:通过条件判断实现临界资源访问的合理性– 条件变量

条件变量:向外提供了一个使线程等待的接口和唤醒线程的接口+pcb的等待队列

当前是否满足获取资源的条件,若不满足,则让执行流等待,等到满足条件能够获取的时候再唤醒执行流

实现同步:两个功能接口:让执行流等待的接口和唤醒执行流的接口

使用接口介绍:

定义条件变量----pthread_cond_t cond;初始化条件变量

pthread_cond_init(pthread_cond_t cond,pthread_condattr_t attr)

pthread_cond_t cond=PTHREAD_COND_INITIALIZER;若资源获取条件不满足时调用接口进行阻塞等待:条件变量是搭配互斥锁一起使用的

pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);-----一直死等别人的唤醒

pthread_cond_timedwait(pthread_cond_t *,pthread_mutex_t *,struct timespec *)----设置阻塞超时时间的等待接口唤醒线程的接口:

pthread_cond_signal(pthread_cond_t *)----唤醒至少一个等待的线程

pthread_cond_broadcast(pthread_cond_t *)----唤醒所有等待的线程销毁条件变量:

pthread_cond_destroy(pthread_cond_t );

示例:

注意事项:

条件变量使用中对条件的判断应该使用while循环多种角色线程应该使用多个条件变量

第一种情况:多个顾客线程,因为没有饭等待,这时候一个厨师做了一碗饭,去唤醒顾客(至少唤醒一个),但是顾客只能有一个加锁,其他两个顾客线程卡在加锁这里,吃完饭后解锁,但是这时候加上锁的不一定是厨师线程,有可能是等待的两个顾客线程,那这个顾客线程就在没有饭的情况下吃了一碗饭----逻辑错误。那么避免这种逻辑错误,条件的判断应该是一个循环判断,顾客线程被唤醒加锁成功后重新判断有没有饭,没有休眠,有就等待

第二种情况:一个顾客线程吃完饭,一条队列上有3个厨师线程,顾客线程进入队列,厨师做好饭,唤醒的可能就不是顾客线程,被唤醒的厨师线程发现有饭,重新休眠,导致程序阻塞。解决方案----不同角色的线程应该挂在不同的等待队列上等待,唤醒的时候,就有目的性的唤醒,因此多个角色需要使用多个条件变量。

标签: #线程安全和非线程安全的区别