(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~https://blog.csdn.net/ky233?type=blog
点个关注不迷路⌯'▾'⌯
目录
一、线程同步的概念
二、条件变量
1.概念
2.接口
1.pthread_cond_init
2.pthread_cond_wait
3.pthread_cond_signal
3.代码演示
一、线程同步的概念
在我们的抢票代码中,会出现某一个线程一直在抢票的时候,这虽然没错,但是这是不合理的
- 会频繁的申请到资源,让别人没办法
- 会让其他线程一直在轮询检测,太过于浪费资源了
- 会造成别人饥饿的问题
所以我们就要引入线程同步
主要是为了解决,访问临界资源合理性问题的!
按照一定的顺序,进行临界资源的访问,叫做线程同步!
二、条件变量
1.概念
当我们申请临界资源前,要先对临界资源做检测,做检测的本质也是访问临界资源!
所以结论,对临界资源的检测,也是一定是需要在加锁和解锁之间的!所以,常规方式要检测条件是否就绪,就注定了我们要频繁的申请和释放锁!
那么有没有办法让我们的线程不这样做呢?
- 让我们的线程不要频繁的自己检测,让他进行等待
- 当条件就绪的时候,通知我们的线程,这时候再进行资源的申请和访问!
- 所以我们的条件变量就诞生了!
2.接口
1.pthread_cond_init
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
全局的或静态的和互斥锁是一样的!
- 参数一:要初始化的条件变量
- 参数二:一些属性,目前设置为nullptr
- 用来条件变量的初始化
2.pthread_cond_wait
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
- 参数一:等待某种条件
- 参数二:是一把锁,为了保证我们临界资源安全的一把锁
- 用于等待条件满足
3.pthread_cond_signal
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
- 有两个接口
- 第一个:可能不止有一个资源在等资源就绪,把所有线程全部唤醒
- 第二个:代表的是唤醒指定的线程
注意:以上接口返回值全都是成功为0,失败为错误码
3.代码演示
#define TNUM 4
typedef void (*func_t)(const string &name,pthread_mutex_t *pmtx,pthread_cond_t *pcond);
volatile bool quit =false;class ThreadDate
{
public:ThreadDate(const string &name, func_t func,pthread_mutex_t *pmtx,pthread_cond_t *pcond): name_(name), func_(func), pmtx_(pmtx),pcond_(pcond) {}string name_;func_t func_;pthread_mutex_t *pmtx_;pthread_cond_t *pcond_;};void func1(const string &name,pthread_mutex_t *pmtx,pthread_cond_t *pcond)
{while(!quit){//wait一定要在加锁和解锁之间wait,这是因为我们需要检测临界资源是否就绪的,而检测就是在访问临界资源!所以要加锁之后才能waitpthread_mutex_lock(pmtx);//条件变量检测临界资源//if("资源是否就绪")//不就绪pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞//cout << name << "播放" << endl;pthread_mutex_unlock(pmtx);}
}void func2(const string &name,pthread_mutex_t *pmtx,pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "下载" << endl;pthread_mutex_unlock(pmtx);}
}void func3(const string &name,pthread_mutex_t *pmtx,pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "刷新" << endl;pthread_mutex_unlock(pmtx);}
}void func4(const string &name,pthread_mutex_t *pmtx,pthread_cond_t *pcond)
{while(!quit){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "扫描信息" << endl;pthread_mutex_unlock(pmtx);}
}void *Entry(void *args)
{ThreadDate *td=(ThreadDate*)args;td->func_(td->name_,td->pmtx_,td->pcond_);delete td;return nullptr;}int main()
{pthread_mutex_t mtx;pthread_cond_t cond;pthread_mutex_init(&mtx,nullptr);pthread_cond_init(&cond,nullptr);pthread_t tids[TNUM];func_t funcs[TNUM]={func1,func2,func3,func4};//创建线程for(int i = 0;i<TNUM;i++){string name="Thread ";name+=to_string(i+1);ThreadDate *td=new ThreadDate(name,funcs[i],&mtx,&cond);pthread_create(tids+i,nullptr,Entry,(void*)td);}sleep(2);//控制线程,尝试唤醒等待的线程int con = 3;while(con){cout<<"唤醒线程 "<<"唤醒次数: "<<con--<<endl;//pthread_cond_signal(&cond);//一个一个唤醒pthread_cond_broadcast(&cond);//一次性唤醒所有sleep(1);}cout<<"ctrl done"<<endl;quit=true;pthread_cond_broadcast(&cond);//让所有的新线程在执行一次,进行下检测//等待线程for(int i =0;i<TNUM;i++){pthread_join(tids[i],nullptr);cout<<"Thread "<<tids[i]<<"quit"<<endl;}pthread_mutex_destroy(&mtx);pthread_cond_destroy(&cond);return 0;
}
在没有条件变量的时候,新线程会排队等待,我们的主线程在排队唤醒新线程,这样我们就可以让我们的线程在收到信号之后完成不同的问题