Linux多线程(2)线程安全(同步与互斥)

1.线程安全

概念

在多线程程序中,涉及到了对共享资源的操作,则有可能导致数据的二义性,而线程安全指的是,就算对共享资源进行操作也不会导致数据二义。

总结:多线程中对共享资源的操作不会出现问题。

实现:同步与互斥

同步:通过条件控制,让多执行对资源的获取更加合理

同步的实现:条件变量;信号量;

互斥:通过同一时间执行流对资源访问的唯一性,保证访问安全

互斥的实现:互斥锁;

2.互斥

(下面是互斥锁原理)

互斥锁实现互斥:实现对共享资源的唯一访问

本质:就是一个0、1的计数器,通过0/1标记资源的访问状态(0表示不可访问;1表示可访问)

在访问资源之前进行加锁操作(通过状态判断是否可访问,不可访问则阻塞)

在访问资源之后进行解锁操作(将资源状态置为可访问状态,唤醒其他阻塞的线程)

另一个理解:访问之前加锁(获取锁资源-获取不到就阻塞),访问资源完毕解锁(归还锁资源)

多个线程想要实现互斥,必须访问同一个锁,也就意味着锁是一个共享资源

互斥锁操作本身必须是安全的:

(下面是互斥锁本身是安全的原理,不要与上面混淆)

这个时候有个指令exchange,功能为交换cpu指定寄存器与内存的数据

互斥锁的操作:

  1. 先将指定寄存器中的值修改为0

  1. 将寄存器与内存中的数据进行互换

  1. 判断是否符合获取锁的条件或者说判断是否能够加锁

置换操作是一条指令完成的,不可被打断

因为在置换之前,把寄存器的值设置为0了,因此置换之后内存中互斥锁的值就是0;这样就保证了,不管我能不能加锁,至少在我之后的肯定加不了锁

if(寄存器数据 == 1)
    return
else
    阻塞

接口

代码演示

  1. 多线程中共享资源的访问如果不加锁会出现什么问题

黄牛抢票例子:有个火车站抢票系统,用全局变量ticket保存票数,4个黄牛抢票

因为判断有无票和抢票过程不是原子性,不是一次完成的,中间可能被打断其他的线程也强到了

解决方案:将判断有无票与抢票过程保护起来,中间不能被打断

部分代码,程序为xshell中 xianchen.c

  1. 如何使用互斥锁来保护临界区(共享资源的访问过程)

//概念:共享资源/临界资源;临界区-访问共享资源的这部分代码

死锁

预防死锁:破坏死锁产生的必要条件

1和2是互斥锁的要义所在,无法破坏

具体操作代码的时候要多注意:

  1. 多个线程间加锁顺序保持一致--尽可能预防环路产生的条件

  1. 采用非阻塞加锁,如果加不上锁,则把已经加锁成功的释放掉--破坏请求与保持条件

避免死锁:具体采取的解决方案

  1. 银行家算法

  1. 死锁检测算法

3.同步

通过条件控制让多线程对资源的获取更加合理

互斥只能保证安全,不能保证合理

同步主要是保证合理,不一定保证安全

资源获取的合理:通常指的是有资源才能处理,没资源就阻塞,等有资源了再被唤醒再处理

条件变量:提供了一个pcb等待队列以及阻塞和唤醒线程的接口

思想:如果一个线程不满足获取资源的条件,则通过阻塞接口阻塞线程

一个线程促使资源获取的条件满足了,则通过唤醒接口唤醒线程

注意:条件变量本身并不知道什么时候该阻塞,什么时候该唤醒,他只是提供接口

条件变量和互斥锁是搭配使用的

举例::

这时候形成新的死锁--卡住的--

解决方案:阻塞这一步的解锁和陷入休眠必须是原子操作(一步完成,不被打断)

操作接口

因条件的判断不能使用if语句,而是使用while语句,但是使用while语局程序卡死

猜你喜欢

转载自blog.csdn.net/weixin_59215611/article/details/130584245