(。・∀・)ノ゙嗨!你好这里是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,这是因为我们需要检测临界资源是否就绪的,而检测就是在访问临界资源!所以要加锁之后才能wait
pthread_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;
}
在没有条件变量的时候,新线程会排队等待,我们的主线程在排队唤醒新线程,这样我们就可以让我们的线程在收到信号之后完成不同的问题