1.线程安全
概念
在多线程程序中,涉及到了对共享资源的操作,则有可能导致数据的二义性,而线程安全指的是,就算对共享资源进行操作也不会导致数据二义。
总结:多线程中对共享资源的操作不会出现问题。
实现:同步与互斥
同步:通过条件控制,让多执行对资源的获取更加合理
同步的实现:条件变量;信号量;
互斥:通过同一时间执行流对资源访问的唯一性,保证访问安全
互斥的实现:互斥锁;
2.互斥
(下面是互斥锁原理)
互斥锁实现互斥:实现对共享资源的唯一访问
本质:就是一个0、1的计数器,通过0/1标记资源的访问状态(0表示不可访问;1表示可访问)
在访问资源之前进行加锁操作(通过状态判断是否可访问,不可访问则阻塞)
在访问资源之后进行解锁操作(将资源状态置为可访问状态,唤醒其他阻塞的线程)
另一个理解:访问之前加锁(获取锁资源-获取不到就阻塞),访问资源完毕解锁(归还锁资源)
多个线程想要实现互斥,必须访问同一个锁,也就意味着锁是一个共享资源
互斥锁操作本身必须是安全的:
(下面是互斥锁本身是安全的原理,不要与上面混淆)
这个时候有个指令exchange,功能为交换cpu指定寄存器与内存的数据
互斥锁的操作:
先将指定寄存器中的值修改为0
将寄存器与内存中的数据进行互换
判断是否符合获取锁的条件或者说判断是否能够加锁
置换操作是一条指令完成的,不可被打断
因为在置换之前,把寄存器的值设置为0了,因此置换之后内存中互斥锁的值就是0;这样就保证了,不管我能不能加锁,至少在我之后的肯定加不了锁
if(寄存器数据 == 1)
return
else
阻塞
接口
代码演示
多线程中共享资源的访问如果不加锁会出现什么问题
黄牛抢票例子:有个火车站抢票系统,用全局变量ticket保存票数,4个黄牛抢票
因为判断有无票和抢票过程不是原子性,不是一次完成的,中间可能被打断其他的线程也强到了
解决方案:将判断有无票与抢票过程保护起来,中间不能被打断
部分代码,程序为xshell中 xianchen.c
如何使用互斥锁来保护临界区(共享资源的访问过程)
//概念:共享资源/临界资源;临界区-访问共享资源的这部分代码
死锁
预防死锁:破坏死锁产生的必要条件
1和2是互斥锁的要义所在,无法破坏
具体操作代码的时候要多注意:
多个线程间加锁顺序保持一致--尽可能预防环路产生的条件
采用非阻塞加锁,如果加不上锁,则把已经加锁成功的释放掉--破坏请求与保持条件
避免死锁:具体采取的解决方案
银行家算法
死锁检测算法
3.同步
通过条件控制让多线程对资源的获取更加合理
互斥只能保证安全,不能保证合理
同步主要是保证合理,不一定保证安全
资源获取的合理:通常指的是有资源才能处理,没资源就阻塞,等有资源了再被唤醒再处理
条件变量:提供了一个pcb等待队列以及阻塞和唤醒线程的接口
思想:如果一个线程不满足获取资源的条件,则通过阻塞接口阻塞线程
一个线程促使资源获取的条件满足了,则通过唤醒接口唤醒线程
注意:条件变量本身并不知道什么时候该阻塞,什么时候该唤醒,他只是提供接口
条件变量和互斥锁是搭配使用的
举例::
这时候形成新的死锁--卡住的--
解决方案:阻塞这一步的解锁和陷入休眠必须是原子操作(一步完成,不被打断)
操作接口
因条件的判断不能使用if语句,而是使用while语句,但是使用while语局程序卡死