同步概念
在之前的博客中有介绍过线程互斥的概念,为了能安全的访问临界区资源,Linux可以对临界区资源进行加锁操作,但是这种操作可能会导致某个线程反复的申请锁,从而使其他线程没办法申请到锁,导致线程饥饿问题。为了解决这个问题,Linux引入了同步这个概念。
同步指的是在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效的避免饥饿问题。
当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。例如一个线程访问队列时,发现队列为空,它只能等待,直到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。
竞态条件则是因为时序问题,而导致程序异常,这就被称为竞态条件。下面来介绍一下Linux下条件变量相关的接口函数及其使用。
接口函数
条件变量初始化及销毁
从man手册中可以看到,这里的条件变量也是一种特殊的数据类型:pthread_cond_t
条件变量的初始化还可以使用宏来进行初始化,例如:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
使用函数进行初始化的情况,需要传入两个参数:
第一个参数是要初始化的条件变量
第二个参数则是直接设置为nullptr即可
使用函数销毁条件变量的时候,只需要传入需要销毁的条件变量即可。
条件变量也是需要申请和销毁一一对应来进行手动释放的。
等待
这里的等待即等待条件满足的意思,需要传入两个参数:
第一个参数需要传入相应的条件变量
第二个参数则是传入互斥量,也就是锁
这个函数的功能是让线程等待条件满足,等待时候会先将线程挂起,并且将锁释放,等待唤醒条件。
唤醒等待
这里唤醒等待有两个接口函数,他们都是只需要传入相应的条件变量来进行唤醒即可。
其中
pthread_cond_broadcast
该函数可以唤醒所有等待的线程
而
pthread_cond_signal
该函数只能唤醒一个等待的线程即当前等待的线程
唤醒后该线程在争抢锁的时候拥有优先权。下面通过一段测试代码来进行测试信号量的使用
测试代码
如下代码是通过master线程来控制worker线程,master线程每隔1秒唤醒一个worker线程,让线程按照顺序来依次执行代码,这样可以避免某个进程反复的争夺锁,从而导致线程饥饿问题。
#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>
pthread_mutex_t mtx;
pthread_cond_t cond;
//ctrl控制work线程,让他定期运行
void* ctrl(void* args)
{
std::string name=(char*)args;
while(true)
{
//唤醒在条件变量下等待的一个线程
//在cond等待队列中等待的第一个线程
pthread_cond_signal(&cond