Linux---线程安全

线程安全

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

线程安全的实现

  • 同步:通过条件判断,实现对临界资源访问的时序合理性。
  • 互斥:通过唯一访问,实现对临界资源访问的安全性。
    同步与互斥也可以实现进程之间的安全,信号量不但能实现同步,也能实现同步。

同步的实现

条件变量实现
条件变量:实现同步的思路向用户提供两个接口(一个是负责让线程陷入阻塞休眠的接口,一个是负责唤醒线程)+ pcb等待队列

实质

通过条件判断,什么时候能够使线程访问临界资源,若不能访问就使得线程阻塞,若能够访问,就唤醒线程,实现线程对临界资源访问的合理性。

消费者与生产者:
在这里插入图片描述
注意:条件变量只是提供了等待与唤醒的接口,本身并没有具备条件判断的功能,意味着:条件变量判断是需要用户自己完成
条件变量的接口功能

  • 定义条件变量 pthread_cond_t cond
  • 初始化条件变量
pthread_cond_init(pthread_cond_t *cond,pthread_cond_condattr_t *attr)
cond = PTHREAD_COND_INITALIZER
  • 使一个线程等待的接口:pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex) ,该接口有三个子操作,a:解锁 b:挂起等待 c:加锁
	pthread_cond_timewait(pthread_cont_t *cond,pthread_mutex_t *mutex,struct timespec *abstime);
	
	限制等待时长的阻塞条件:等待一段指定的时间,时间到了调用就报错返回-ETIMEOUT

线程等待接口会将不能访问临界资源的线程加入到等待队列中。条件变量搭配互斥锁使用的,条件变量并不提供条件判断的功能,需要用户自己去判断,(通常条件的判断是一个临界资源的访问),因此这个资源的访问就需要受保护,所以需要配合护互斥锁使用。

  • 一个唤醒线程的接口
    pthread_cond_signal(pthread_cond *cond); 唤醒至少一个线程
    pthread_cond_broadcast(pthread_cond *cond)唤醒所有的等待线程
  • 若不使用条件则销毁资源:pthread_destroy(pthread_cond *cond)

互斥的实现原理

同一时间只有一个执行流能够访问临界资源。

  • 对临界资源进行标记:没人访问的时候标记为1,表示可以访问,有人访问的时候标记为0,表示不能访问。
  • 在对临界资源进行访问的时候,先判断是否能够进行访问,不能访问则线程休眠。

互斥实现的技术:互斥锁

互斥锁(mutex)
本质就是一个计数器,只要0/1的计数器,用于标记资源当前的访问状态,1表示可访问,0表示不可访问。

加锁
每个线程在访问临界资源之前都要先访问互斥锁。

互斥锁原理
在这里插入图片描述

  • 1:将cpu寄存器上的值修改为0,然后与内存中计数器进行数据交换(意味着这时候计数器变成0,谁来访问,都是一种不可访问状态)
  • 2:若寄存器交换数据为0,则表示当前不可访问,则将pcb状态置为阻塞状态,线程将被挂起等待。
  • 3:若寄存器交换后的数值为1,则表示当前可以访问,则加锁操作直接返回,表示加锁成功,继续访问资源。
  • 4:访问完数据之后,进行解锁(将内存中的计数器的值修改回去)。

互斥锁通过在访问资源前加锁,访问结束后解锁,从而实现互斥锁计数的原子性。

互斥锁本身就是一个临界资源

互斥锁的操作流程

  • 定义互斥锁变量 pthread_mutex_t mutex;
  • 初始化互斥锁
mutex = PTHREAD_MUTEX_INITALIZER
int pthread_mutex_init(pthread_mutex *mutex,pthread_mutexattr_t *attr)
mutex:互斥锁变量首地址
attr:互斥锁属性,通常为NULL
  • 在对临界资源访问之前,先加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);阻塞加锁,如果不能加锁,则一直等待
int pthread_mutex_trylock(pthread_mutex_t *mutex);非阻塞加锁,如果不能加锁,则报错返回
  • 在对临界资源访问完毕之后,解锁操作(把状态标记为可访问)
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 不使用锁,最终要释放资源,销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t mutex);

锁的初始化一定要在创建线程之前。
加锁后,在任意有可能退出的地方解锁
锁的销毁,一定是保证没有人使用互斥锁的时候

死锁

产生原因
多个执行流对锁资源进行争抢访问,但是由于推进顺序不当,从而导致互相等待,最后造成程序流程无法执行完成。

死锁产生的必要条件

  • 互斥条件:一个锁不能同时给多个执行流加,一个执行流加了锁,别人就不能加这个锁。同一时间,只有一个线程能够加锁。
  • 不可剥夺资源:一个线程加了锁,就必须自己解锁,别的线程没有解锁的权力。
  • 请求与保持条件:拿个这个锁,去抢别的锁
  • 环路等待条件:线程1抢到锁A,然后去抢锁B,线程2抢到锁B,然后去抢锁A

如何避免产生死锁

  • 银行家算法
  • 死锁判断算法

猜你喜欢

转载自blog.csdn.net/qq_42708024/article/details/104443647