【Linux】线程安全:互斥锁、条件变量、信号量、死锁

线程安全是指多个线程对于临界资源的访问是安全的,而要实现线程安全,我们要采用同步与互斥的方式。

  • 互斥锁
    互斥锁本质上是一个只有0和1的计数器,本身也是一个临界资源(采用一步置换的方法保持自身操作的原子性)。互斥锁用于标记临界资源的访问状态,每个线程在访问临界资源之前,都要先访问互斥锁,判断当前是否是可访问状态,如果是可访问状态,则先将访问状态置为不可访问,再去访问临界资源,访问完成后再将访问状态置为可以访问,若是不可访问状态,则阻塞或者是报错。
    要使用互斥锁,我们可以调用以下接口。
//创建互斥锁
pthread_mutex_t mutex; 
//初始化互斥锁
pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutex_t *attr);
//加锁,可以加锁则返回,不可以加锁则阻塞
pthread_mutex_lock(pthread_mutex_t *mutex);
//加锁,不能加锁则报错返回
pthread_mutex_trylock(pthread_mutex_t *mutex);
//解锁
pthread_mutex_unlock(pthread_mutex_t *mutex);
//销毁互斥锁(互斥锁资源释放)
pthread_mutex_destroy(pthread_mutex_t *mutex);

  • 条件变量
    可以通过条件变量进行线程同步的实现,条件变量提供了使线程阻塞和唤醒线程的接口,但具体阻塞什么线程,什么时候唤醒线程都需要程序员自己控制合理的时序逻辑,在合适的时候调用接口。
    要使用条件变量,我们可以调用以下接口。
//定义条件变量变量
pthread_cond_t cond;
//初始化条件变量变量
pthread_cond_init(pthread_cond_t *,pthread_condattr_t *);
//在一个线程不满足资源访问的情况下,使线程阻塞,包含了被唤醒后的加锁mutex,其中解锁和进入休眠是一步原子操作,不会被打断。
pthread_cond_wait(pthread_cond_t *,pthread_mutex_t *);
//在一个线程不满足资源访问的情况下,使线程阻塞,是限时的操作
pthread_cond_timedwait(pthread_cond_t *,pthread_mutex_t *,struct timespc *timeout);
//资源访问条件满足后,至少唤醒一个阻塞的线程
pthread_cond_signal(pthread_cond_t *);
//资源访问条件满足后,唤醒所有阻塞的线程
pthread_cond_broadcast();
//销毁条件变量
pthread_cond_destroy(pthread_cond_t *);

使用条件变量时,有以下注意事项
1.条件判断必须使用循环操作(使用whlie语句,不能使用if语句,多对多时可能会出错)
2.多种角色应该使用多个条件变量,让不同的角色等待在不同的条件变量队列,便于分角色唤醒,防止唤醒角色错误。

  • 信号量
    信号量在IPC的博客中以及提及,本质上就是一个计数器+pcb等待队列,可以用来实现进程和线程的同步与互斥。在这里我们介绍posix标准信号量。
//定义信号量
sem_t;
//初始化信号量,pshared为0用于线程间,为非0用于进程间,val用于初始化计数器的初值
sem_init(sem_t *sem,int pshared,int val);
//在临界资源访问/获取之前进行p操作
sem_wait(sem_t *sem); //当前计数<=0阻塞操作
sem_trywait(sem_t *sem); //当前计数<=0则报错返回,非阻塞操作
sem_timedwait(sem_t *sem,struct timespec *timeout);//限时阻塞操作,表示在指定时间内若都无法满足条件,等大超时后则报错返回
//V操作,资源产生后,进行计数+1,唤醒阻塞
sem_post(sem_t *sem);
//销毁信号量
sem_destroy(sem_t *sem);
  • 死锁
    程序流程无法继续推进的情况,多个线程对锁资源进行争抢获取,但因为流程推进顺序不当,造成互相等待,最终流程无法推进。
    死锁产生的四个必要条件:
    1.互斥条件
    2.不可剥夺条件
    3.请求与保持条件
    4.环路等待条件
    如果符合这四个条件则一定会产生死锁,而如果产生死锁,不一定具有这四个条件,可能是因为其他操作导致的死锁。
    死锁对于程序的运行来说是有非常大的危害,所以我们要尽量利用统一加锁顺序非阻塞加锁等预防死锁的产生,同时还有利用银行家算法死锁检测算法等来避免产生死锁。

信号量与条件变量的区别:
信号量通过自身计数实现判断,不需要搭配互斥锁。
条件变量需要程序员自身进行条件判断,需要搭配互斥锁使用。
信号量与互斥锁的区别:
信号量可以实现互斥,但更多用于同步的完成。

猜你喜欢

转载自blog.csdn.net/weixin_45177279/article/details/115254857