Linux/多线程的同步与互斥

线程安全:多个执行流对资源进行争抢访问,但不会产生数据二义性
线程安全的实现:同步,互斥
同步:通过条件判断实现对临界资源访问的合理性
互斥:通过同一时间对临界资源的唯一访问,实现对临界资源访问的安全性

互斥锁

互斥的实现:互斥锁

在多任务处理的操作系统中,多个执行流可能同时要使用某个数据,就假如我们现实中有一台打印机,有许多人要使用,如果不加以控制,比如说排队,上一个人还没有使用完下一个人就是用,打印出来的东西肯定就是混乱的。
在线程中也有一个处理方式,叫做互斥锁。
互斥锁是一种简单的加锁的方式来控制对临界资源的访问,互斥锁只有两种状态:上锁(lock)和解锁(unlock)
互斥锁:互斥锁本身是一个0/1的计数器,描述了一个临界资源的访问状态,每个执行流在访问临界资源之前都需要判断这个临界资源的访问状态,如果不可以访问,则执行流挂起等待,如果可以访问,则让执行流访问临界资源,但在访问期间需要将资源状态改为不可访问状态。这期间如果有其他执行流想要访问,则不被允许。在执行流访问完临界资源退出前会将资源状态改变会可访问状态。

互斥锁特性:
1.原子性:将一个互斥锁的操作定义成原子操作,因为一个互斥锁也相当于一个临界资源,大家都可以访问,在使用时必须保证操作的原子性。
在这里插入图片描述

如果有多个线程同时去获取锁资源,这时Mutex的值判断>0,一个线程在将mutex置0的过程中,另一个线程也可以进入,那么这些线程都会以为自己获得了锁。然而线程在任何时候都可能会被触发导致切换,每个线程运行时,mutex都会加载入cpu,同时访问临界资源,这会导致有多个mutex副本,还是无法保证数据的一致性。
因此一般先将寄存器中的值置为0,线程得到锁后进入寄存器,直接进行一次数据交换,将寄存器中0与Mutex的1交换,操作一次完成,保证了操作的原子性。

唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有线程可以锁定这个互斥量
非繁忙等待:如果一个线程已经锁定了一个互斥量,另一个线程又试图去锁定这个互斥量,这个线程将会被挂起等待,直到第一个线程解除对互斥量的锁定,第二个线程则被唤醒执行,并锁定这个互斥量。

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

  1. 定义互斥锁变量
pthread_mutex_t mutex
  1. 初始化互斥锁变量
pthread_mutex_init(pthread_mutex_t* mutex,pthread_mutexattr_t* attr)
  1. 在访问临界资源前进行加锁访问
pthread_mutex_lock(pthread_mutex_t* mutex)
pthread_mutex_trylock(pthread_mute_t* mutex)
  1. 在临界资源访问结束后进行解锁
pthread_mutex_unlock(pthread_mutex_t* mutex)
  1. 销毁互斥锁
pthread_mutex_destroy(pthread_mutex_t* mutex)

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

死锁产生的原因

  1. 资源竞争
    如果一个线程先后两次调用了lock,第二次调用时锁已经被占用,那么这个线程会被挂起等待别的线程去释放锁,但是使用锁的是自己并且自己已经被挂起等待,就会一直处于挂起等待状态,造成死锁。
  2. 线程推进顺序不当
    假设线程1使用了锁A,线程2使用了锁B,线程1此时想要去使用锁B会被挂起等待,线程2想要去使用锁A会被挂起等待,于是两个线程都处于挂起等待状态了

死锁产生的条件

  1. 互斥:我使用了锁,别人不能使用锁
  2. 请求与等待:我使用了锁,别人只能等待我解锁才能使用,否则等待
  3. 请求与保持条件:我使用了锁A,我去请求锁B,并不会释放锁A
  4. 环路等待条件:我使用了锁A,请求B锁,另一个人使用了锁B,去请求锁A

为了避免死锁的产生,在使用互斥锁时应该尽量避免同时获得多个锁

同步

条件变量

条件变量:线程在满足资源的访问条件的时候才能去访问资源,否则就挂起线程,直到满足条件后再去唤醒线程
因为条件变量只提供了使线程等待和唤醒线程的接口,因此访问条件是否满足要在我们使用时自行判断。
操作代码
定义条件变量

pthread_cond_t cond;

初始化条件变量

pthread_cond_init(pthread_cond_t* cond,phread_condattr_t* attr)
//cond:条件变量
//attr:属性,一般置NULL

使线程挂起休眠(需要搭配互斥锁一起使用),因为判断条件是否满足的条件本身就是一个临界资源

pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex)
//一直等待别人唤醒,不唤醒则一直等待
pthread_cond_timedwait(pthread_cond_t* cond,pthread_mutex_t mutex,struct timespec)
//等待指定时间,不唤醒则时间到了自动唤醒

唤醒线程

pthread_cond_signal(pthread_cond_t* cond)
//唤醒至少一个等待线程
pthread_cond_broadcast(pthread_cond_t* cond)
//唤醒所有等待线程

销毁条件变量

pthread_cond_destroy(pthread_cond_t* cond)

注意:
1.条件变量使用对条件的判断需要使用while语句
2.多个角色线程应该使用多个条件变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值