多线程——线程安全

文章目录

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

线程安全

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

线程安全的实现:
同步:通过条件判断保证对临界资源访问的合理性
互斥:通过同一时间对临界资源访问的唯一性实现临界资源访问的安全性

互斥的实现:互斥锁

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

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

  1. 定义互斥锁变量 pthread_mutex_t mutex;
  2. pthread_mutex_init(pthread_mutex_t mutex,pthread_mutexattr_t attr);pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;定义并且初始化
  3. 在访问临界资源之前进行加锁操作(不能加锁则等待,可以加锁则修改资源状态,然后调用返回,访问临界资源)
    pthread_mutex_lock(pthread_mutex_t mutex);
    pthread_mutex_trylock(pthread_mutex_t *mutex);(非阻塞:不能加锁立即报错返回)
    挂起等待:将线程状态置为可中断休眠状态–表示当前休眠;被唤醒:将线程状态置为运行状态
  4. 在临界资源访问完毕之后进行解锁操作(将资源状态置为可访问,将其他执行流唤醒)
    pthread_mutex_unlock(pthread_mutex_t mutex);
  5. 销毁互斥锁
    pthread_mutex_destroy(pthread_mutex_t mutex)

所有的执行流都需要通过同一个互斥锁实现互斥,意味着互斥锁本身就是一个临界资源,大家都会访问
如果互斥锁本身的操作都不安全如何保证别人安全,所以互斥锁本身的操作首先必须是安全的------互斥锁自身计数的操作是原子操作

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

死锁

多个执行流对锁资源进行争抢访问,但是因为访问推进顺序不当,造成互相等到最终导致程序流程无法继续推进,这时候就造成了死锁。
死锁实际是一种程序流程无法继续推进,卡在某个位置的一种概念。
死锁产生的必要条件:
1.互斥条件
2.不可剥夺条件:我加的锁,别人不能解,只有我能解锁
3.请求与保持条件:我加了A锁,然后去请求B锁;如果不能对B加锁,则也不释放A锁
4.环路等待条件:我加了A锁,然后去请求B锁,另一个人加了B锁,然后去请求A锁。
死锁的预防:破坏死锁产生的必要条件(主要避免3和4两个条件的产生)
死锁的避免:银行家算法

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

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

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

条件变量:向外提供了一个使线程等待的接口和唤醒线程的接口+pcb的等待队列
当前是否满足获取资源的条件,若不满足,则让执行流等待,等到满足条件能够获取的时候再唤醒执行流
实现同步:两个功能接口:让执行流等待的接口和唤醒执行流的接口
使用接口介绍:

  1. 定义条件变量----pthread_cond_t cond;
  2. 初始化条件变量
    pthread_cond_init(pthread_cond_t cond,pthread_condattr_t attr)
    pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
  3. 若资源获取条件不满足时调用接口进行阻塞等待:条件变量是搭配互斥锁一起使用的
    pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);-----一直死等别人的唤醒
    pthread_cond_timedwait(pthread_cond_t *,pthread_mutex_t *,struct timespec *)----设置阻塞超时时间的等待接口
  4. 唤醒线程的接口:
    pthread_cond_signal(pthread_cond_t *)----唤醒至少一个等待的线程
    pthread_cond_broadcast(pthread_cond_t *)----唤醒所有等待的线程
  5. 销毁条件变量:
    pthread_cond_destroy(pthread_cond_t );
    示例:


注意事项:

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值