线程安全概念
线程安全的概念就是多个执行流对临界资源进行争抢访问但是不会出现数据二义性。
线程安全的实现主要是通过同步与互斥。
- 同步:通过条件判断保证对临界资源访问的合理性
- 互斥:通过同一时间对临界资源访问的唯一性实现对临界资源访问的安全性
同步与互斥详细概念
相交进程之间的关系主要有两种,同步与互斥。所谓互斥,是指散步在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它 们之中的任一程序片段 只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的 某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。
显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。
也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!
总结
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
我们可以通过互斥锁,条件变量,信号量来实现同步与互斥,下面对其逐一介绍。
互斥锁
互斥锁原理
互斥锁本质上是一个0/1的计数器,描述了临界资源当前是否可以访问,每当有执行流想要访问临界资源的时候都需要先进行判断,可以访问则加锁(将临界资源置为不可访问状态),然后自己访问,最后解锁。当不能访问的时候,就会进行等待。
互斥锁可以被多个执行流争抢的访问使用,因此互斥锁的读写操作自身必须保证是安全的。
互斥锁通过保证自身计数是原子操作来实现自身的安全性。
每当线程访问互斥锁的时候,寄存器就会将自己先置为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_destory(pthread_mutex_t *mutex)
死锁
当一个进程中有多个互斥锁,当多个执行流对锁资源进行争抢访问,但是由于推进顺序不当,造成互相等待无法继续推进,这时候就造成了死锁。
死锁实际上就是,程序卡在某个地方,没办法继续执行的概念。
死锁产生的必要条件:有一个不满足就不会造成死锁。
- 互斥条件:我上了锁,其他人不能上锁
- 不可剥夺条件:我上了锁,别人不能解,只有我可以解锁
- 请求与保持条件:我加了A锁,去请求B锁,如果不能对B加锁,也不释放A锁
- 环路等待条件:我上了A锁,去请求B,另一个人上了B来请求A
死锁的预防:
主要破环3,4条件即可。
死锁的避免:
死锁检测算法/银行家算法
代码实现
以模拟抢票为例,代码一是没有使用互斥锁,执行结果中抢到了-1/-2.着显然不合常理。
代码而使用互斥锁进行对ticket的保护,是正确代码。
代码一:
执行结果:
正确代码:
执行结果:
条件变量
条件变量搭配互斥锁就可以实现不同执行流之间的同步与互斥。
条件变量的实现方式:当指定执行流不满足执行条件时,就让该执行流陷入睡眠,进入pcb等待队列,等待条件满足之后唤醒执行流。
函数接口
1.定义条件变量
pthread_cond_t cond
2.初始化条件变量
pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t * attr)
cond = PTHREAD_COND_INITIALIZER
3.使线程挂起休眠
pthread_cond_wait(pthread_cond_t *cond, pthread_mutex *mutex)
pthread_cond_timedwait(pthread_cond_t