文章目录
- 一. 为什么要线程同步
- 二. 条件变量
- 1. 条件变量的使用
- 2. 简单使用
- 结束语
一. 为什么要线程同步
通过互斥量,也就是加锁解锁,我们可以实现线程互斥,但是当访问的临界区代码较少时,线程执行会出现不停加锁解锁的情况。这样一会导致线程效率降低,二会导致其他线程无法访问到临界资源,从而出现饥饿问题
。
为了避免出现不断加锁解锁的情况,我们需要让使用完锁的线程,不能立刻重新申请锁,并且等待锁资源的其他线程,需要按照一定顺序进行等待。这就是线程同步
二. 条件变量
1. 条件变量的使用
当线程互斥地访问某个变量时,它可能会发现,在其他线程改变状态前,它什么也做不了
例如一个队列要读取队列的数据,但是当他发现队列为空时,那么他只能等待,等待其他线程将数据添加到队列中。这种情况就需要用到条件变量
- 同步: 在保证数据安全的前提下,
让线程能够按照某种特定的顺序访问临界资源
,从而有效避免饥饿问题,这就叫做同步 - 竞争条件: 因为时序问题,而导致程序异常,我们称之为竞态条件
Linux中,条件变量是定义的一个联合体嵌套结构体,由操作系统帮我们维护
条件变量初始化
条件变量有两种初始化方式
静态初始化
同互斥量(锁)一样,操作系统提供静态的条件变量
//静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
该方法初始化的静态变量不需要手动销毁。
动态初始化
//动态初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr *restrict attr);
cond:需要初始化的条件变量的指针
attr:条件变量的属性,传nullptr代表使用默认属性
条件变量销毁
条件变量在使用完后同样也需要销毁
//条件变量销毁
int pthread_cond_destroy(pthread_cond_t *cond);
cond:需要销毁的条件变量的指针
因为线程同步是要让访问同一临界区的线程按顺序访问,所以当线程开始运行时,我们需要让他们都先等待,然后由用户指定唤醒,就可以形成一定的顺序
等待条件满足
//等待条件
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
cond:要在这个条件变量上等待
mutex:互斥量。因为等待时,线程是持有锁的,让线程等待意味着不会运行,所以需要解锁。
调用这个函数后,线程会在该条件变量上等待,直到被唤醒。等待前会解锁,唤醒后会加锁。
唤醒等待
//唤醒在该条件变量上等待的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒在该条件变量等待的某一个线程
int pthread_cond_signal(pthread_cond_t *cond);
cond:需要唤醒的条件变量
2. 简单使用
接下来,我们简单做个demo展示一下条件变量的使用
我们创建5个线程,然后让5个线程都在条件变量上等待,然后一个一个唤醒
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string>
using namespace std;
//条件变量
//静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//静态锁
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
//启动函数
void*Action(void*args)
{
string name=static_cast<const char*>(args);
while(true)
{
//申请锁
pthread_mutex_lock(&mutex);
//等待唤醒
pthread_cond_wait(&cond,&mutex);
cout<<name<<" 运行"<<endl;
//解锁
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
int main()
{
pthread_t tids[5];
//创建5个线程
for(int i=0;i<5;++i)
{
//线程名
char *name=new char[32];
snprintf(name,32,"thread-%d",i+1);
pthread_create(tids+i,nullptr,Action,name);
}
sleep(3);
//挨个唤醒
while(true)
{
cout<<"主线程唤醒线程..."<<endl;
pthread_cond_signal(&cond);
sleep(1);
}
for (int i = 0; i < 5; i++)
{
pthread_join(tids[i], nullptr);
}
return 0;
}
我们也可以一次性全部唤醒
//唤醒
while(true)
{
cout<<"主线程唤醒线程..."<<endl;
//pthread_cond_signal(&cond);
pthread_cond_broadcast(&cond);
sleep(1);
}
结束语
感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。