线程安全

线程安全
1. 线程安全概念

线程安全:多个执行流对临界资源进行争抢访问,而不会造成数据二义或者逻辑混乱称这段争抢访问的过程是线程安全。

2. 线程安全的实现

线程安全的实现:如何保证多个执行流对临界资源进行争抢访问而不会造成数据二义(同步+互斥)。
同步:通过条件判断,实现对临界资源访问的时序合理性
互斥:通过唯一访问,实现对临界资源访问的安全性

3.实现互斥原理:只要保证同一时间只有一个执行流能够访问资源

临界资源进行状态标记:没人访问标记为1,表示可访问,有人正在访问的时候,就标记为0,表示不可访问,在对临界资源进行访问之前先进行状态的判断,决定是否能够访问,不能访问则是其休眠。

4. 实现互斥的技术:互斥锁

4.1 互斥锁:其实就是一个计数器,只有0/1的计数器,用于标记资源当前的访问状态1-可访问 0-不可访问,互斥锁想要实现互斥,每个线程在访问临界资源之前都要先访问同一个互斥锁(加锁),意味着互斥锁本身就是一个临界资源
4.2 互斥锁的计数器操作如何实现原子性
1.将cpu寄存器上的值修改为0,然后与内存中计数器进行数据交换(意味着这时候计数器变成0,都不可以访问)。
2.若寄存器交换后数据为0,则表示当前不可访问,将pcb状态置为阻塞状态,线程将被挂起等待。
若寄存器交换后数据为1,则表示当前可以访问,加锁操作直接返回,表示加锁成功–继续可以访问资源。
3.访问完数据之后,要进行解锁操作(将内存中计数器的值在修改回来)。并且互斥锁计数器的改变本身也是一个原子操作
4.3 互斥锁的操作流程
1.定义互斥锁变量
ptread_mutex_t mutex;
2.初始化互斥锁
mutex = PTHREAD_MUTEX_INTIALIZER; int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr);
参数:
mutex:互斥锁变量首地址
attr:互斥锁属性–通常置NULL
3.对临界资源访问之前进行加锁操作–保护对临界资源访问的过程
int pthread_mutex_lock(pthread_mutex_t *mutex);阻塞加锁—如果不能加锁,则一直等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);非阻塞加锁—如果不能加锁,则立即报错返回,若可以加锁,则加锁后返回
4.对临界资源访问完毕之后进行解锁(把状态标记为可访问)
int pthread_mutex_unclock(pthread_mutex_t *mutex);
5.不在使用互斥锁记得释放互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
6.互斥锁的操作流程中,需要注意的:
(1)加锁之后,在任意有可能退出线程的地方记得解锁
(2)锁的初始化一定要在创建线程之前
(3)锁的销毁一定是保证没有使用互斥锁的时候
4.4 注意事项
1.加锁保护的区域,最好只有对临界资源访问的过程—因为保护的越多,执行的所需时间越长。
2.再加锁之后,在任意有可能退出线程的地方都要进行解锁操作—若没有解锁直接退出,有可能造成其他获取锁的线程卡死。
4.5 互斥锁与信号量的区别
信号量是对资源进行计数的计数器·,实现资源的合理性,主要用于实现同步。

5.死锁

5.1 死锁:多个执行流对锁资源进行争抢访问,但是因为推进顺序不当,而导致互相等待,最终造成程序流程无法继续的情况。
5.2 死锁产生的必要条件
1.互斥条件:同一时间只有一个线程能够加锁
2.不可剥夺条件:线程加的锁只有自己能解
3.请求与保持条件:线程1,抢到了锁A,然后去抢锁B,但是抢不到锁B,也不释放锁A
4.环路等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
5.3 如何预防死锁
1.破坏死锁的四个必要条件
2.加锁顺序一致
3.避免锁未释放的场景
4.资源一次性分配
5.4 避免产生死锁:银行家算法/死锁检测

6. 同步的实现:条件变量/信号量

6.1 条件变量
条件变量:实现同步的思路向用户提供两个接口(一个是让线程陷入阻塞休眠的接口,一个是唤醒线程休眠的接口)+pcb等待队列。
条件变量是搭配互斥锁使用的,条件变量只是向外提供了等待与唤醒的接口,并不提供条件判断功能,需要用户去判断(通常条件判断是一个临界资源的访问),这个临界资源的访问,就需要被保护,使用互斥锁保护。
6.2 条件变量提供的接口功能
1.定义条件变量
pthread_cond_t cond;
2.初始化条件变量
pthread_cont_init(pthread_cond_t *cond,pthread_condattr_t *attr);
3.是一个线程等待的接口
pthread_cond_wait(pthread_cont_t *cond,pthread_mutex_t *mutex);
条件变量是搭配互斥锁使用的:条件变量并不提供条件判断功能,需要用户判断(通常条件的判断是一个临界资源的访问),因此这个临界资源的访问就需要保护,使用互斥锁保护。
pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex_t *mutex,struct timespec *abstime);
限制等待时长的阻塞操作:等待一段指定的时间,时间到了调用就会报错返回—ETIMEDOUT
4.一个唤醒线程的接口
pthread_cond_signal(pthread_cond_t *cond);唤醒至少一个等待的线程
pthread_cond_broadcast(pthread_cond_t *cond)唤醒所有等待线程
5.若不使用条件变量则销毁释放资源
pthread_cond_destroy(pthread_cond_t* cond);
6.3 注意事项
1.pthread_cond_wit实现了三步操作:先解锁、陷入休眠、被唤醒后加锁(原子操作)
2.资源访问条件是否满足判断应该是一个循环判断----一个厨师唤醒两个顾客
3.不同的角色应该等待在不同的条件变量上----可能唤醒的是相同角色

6.4 条件变量与信号量的区别
1.条件变量促使线程等待的条件判断需要用户自身完成,而信号量通过自身计数进行判断的。
2.条件变量需要搭配互斥锁一起使用,而信号量不用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值