一、信号量
概念
信号量是一种特殊的变量,可用于线程同步。信号量s是具有非负整数值的全局变量,只能由两种特殊操作(P和V)来处理。
操作
●P(s):
1.s > 0,将s - 1
2.s == 0,挂起该线程,直到 s != 0
●V(s):
将s + 1,如果有其他线程阻塞在P操作等待s变为非0,V操作会唤醒它们中的一个,然后被唤醒的线程将s - 1,完成P操作。
系统调用
●sem_init(sem_t *sem, 信号量类型(进程/线程间共享), 信号量值大小) 初始化信号量
●sem_destroy(sem_t *sem) 销毁信号量
●sem_wait(sem_t *sem):以原子操作的方式将信号量减 1,如果信号量值为 0,则 sem_wait 将被阻塞,直到这个信号量具有非 0 值。
●sem_post(sem_t *sem):以原子操作将信号量值+1。当信号量大于 0 时,其他正在调用sem_wait 等待信号量的线程将被唤醒。
应用
●生产者-消费者问题
对于生产者来说,当缓冲区满,也就是空闲缓冲区个数为0时,此时生产者不能继续向缓冲区写数,必须等待,直到有消费者从满缓冲区取走数后,再次有了空闲缓冲区,生产者才能向缓冲区写数。
对于消费者来说,当缓冲区空时,此时没有数可以被取走,消费者必须等待,直到有生产者向缓冲区写数后,消费者才能取走。并且如果当缓冲区空时,先后有多个消费者均想从缓冲区取数,那么它们均需要等待,此时需要记录下等待的消费者个数,以便缓冲区有数可取后,能将所有等待的消费者唤醒,确保请求取数的消费者最终都能取的数。
也就是说,当多个进程需要协同合作时,需要根据某个信号,判断当前进程是否需要停下来等待;同时,其他进程需要根据这个信息判断是否有进程在等待,或者有几个进程在等待,以决定是否需要唤醒等待的进程。而这个信息,就是信号量。
二、互斥量
概念
又称为互斥锁,主要用于线程互斥。
当进入临界区时,需要获得互斥锁并且加锁;当离开临界区时,需要对互斥锁解锁,以唤醒其他等待该互斥锁的线程。
系统调用
●pthread_mutex_init:初始化互斥锁
●pthread_mutex_destroy:销毁互斥锁
●pthread_mutex_lock:以原子操作的方式给一个互斥锁加锁,如果目标互斥锁已经被上锁,pthread_mutex_lock 调用将阻塞,直到该互斥锁的占有者将其解锁。
●pthread_mutex_unlock:以一个原子操作的方式给一个互斥锁解锁
应用
●资源访问:
一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源。比如对全局变量的访问。
三、条件变量
概念
条件变量,又称条件锁,用于在线程之间同步共享数据的值。条件变量提供一种线程间通信机制:当某个共享数据达到某个值时,唤醒等待这个共享数据的一个/多个线程。
为何需要条件变量
满足条件可以通知,不用轮询条件是否满足
如果没有条件变量,就每次都需要加锁进入临界区去查看条件是否满足,然后再离开。需要不断加锁解锁去判断条件是否满足。条件变量的产生可以不用循环加锁解锁,并且第一时间收到条件满足的通知。
系统调用
●pthread_cond_init函数,用于初始化条件变量
●pthread_cond_destory函数,销毁条件变量
●pthread_cond_signal函数,唤醒一个等待目标条件变量的线程
●pthread_cond_broadcast函数,以广播的方式唤醒所有等待目标条件变量的线程
●pthread_cond_wait函数,用于等待目标条件变量。该函数调用时需要传入 mutex参数(加锁的互斥锁) ,函数执行时,先把调用线程放入条件变量的请求队列,然后将互斥锁mutex解锁,当函数成功返回为0时,表示重新抢到了互斥锁(开始处理),互斥锁会再次被锁上, 也就是说函数内部会有一次解锁和加锁操作.
pthread_cond_wait为什么要解锁再加锁
https://blog.csdn.net/qq_36459662/article/details/114838216
线程同步机制封装类
利用RAII机制,资源获取即初始化,比如锁,构造就是加锁,析构就是解锁。
https://blog.csdn.net/qq_36459662/article/details/106647317
应用和注意点
https://editor.csdn.net/md/?articleId=106901292
条件变量和信号量的区别
●条件变量有广播的功能,可以唤醒等待的所有线程
●信号量、共享内存、消息队列是IPC三剑客,主要用于进程间通信
●条件变量、互斥锁主要用于线程间通信