Linux -- 同步与条件变量
目录
同步
条件变量
pthread_cond_t
pthread_cond_init(初始化条件变量)
pthread_cond_destroy(销毁条件变量)
pthread_cond_wait(线程等待条件变量)
重要提醒
pthread_cond_boardcast(唤醒等待队列中的所有线程)
pthread_cond_singal(唤醒一个正在等待的线程)
代码
条件变量的使用
唤醒一个线程:
广播唤醒线程:
同步
在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,确保这些执行单元以一种有序、非冲突的方式进行交互,从而有效避免饥饿问题,叫做同步。
条件变量
条件变量的主要用途是用于实现同步,在一个或多个线程等待某个特定条件变为真时,可以被阻塞(挂起),直到其他线程通知这个条件已经满足。它提供了一种机制来安全地等待某个条件,并在条件满足时唤醒等待的线程。条件变量通常与互斥锁(
pthread_mutex_t
)一起使用,以保护共享资源和确保线程安全。条件变量其实是一种通知机制 + 等待队列:
- 当某种特定的条件未成立时,线程在等待队列中排队;
- 当条件成立时,可以选择唤醒队头的线程或者队列中的所有线程,继续执行线程任务。
pthread_cond_t
线程库中用于条件变量的一种数据类型。
pthread_cond_init(初始化条件变量)
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
cond
:指向要初始化的条件变量的指针。
attr
:指向条件变量属性对象的指针。如果只需要默认属性,可以传递NULL
。
返回值:
- 成功时,返回 0。
- 失败时,返回错误编号。
pthread_cond_destroy(销毁条件变量)
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
cond
:指向要销毁的条件变量的指针。
返回值:
- 成功时,返回 0。
- 失败时,返回错误编号。
pthread_cond_wait(线程等待条件变量)
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
cond
:指向要等待的条件变量的指针。
mutex
:指向已经加锁的互斥锁的指针。该互斥锁应该保护着与条件相关的共享数据。
返回值:
- 成功时,返回 0。
- 失败时,返回错误编号。
重要提醒
- 在调用
pthread_cond_wait
时,当前线程必须持有由mutex
指向的互斥锁。- 调用此函数后,
pthread_cond_wait
会自动释放互斥锁,并将当前线程置为等待状态直到某个其他线程调用pthread_cond_signal
或pthread_cond_broadcast
来唤醒它。- 当前线程被唤醒后,
pthread_cond_wait
会重新尝试获取互斥锁;只有成功获取到互斥锁之后,函数才会返回。- 返回时,互斥锁再次被当前线程持有。
pthread_cond_boardcast(唤醒等待队列中的所有线程)
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
cond
:指向要广播信号的条件变量的指针。
返回值:
- 成功时,返回 0。
- 失败时,返回错误编号。
- 当调用
pthread_cond_broadcast
时,所有当前正在等待该条件变量的线程都会被唤醒。- 每个被唤醒的线程在继续执行之前会重新尝试获取与条件变量关联的互斥锁。
- 注意,这并不意味着所有被唤醒的线程都会立即运行;它们仍然需要按照操作系统的调度算法来获得CPU时间。
pthread_cond_singal(唤醒一个正在等待的线程)
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
cond
:指向要发出信号的条件变量的指针。
返回值:
- 成功时,返回 0。
- 失败时,返回错误编号。
- 当调用
pthread_cond_signal
时,如果有线程正在等待该条件变量,则会唤醒其中一个线程。- 被唤醒的线程在继续执行之前会重新尝试获取与条件变量关联的互斥锁。
- 如果没有线程在等待该条件变量,那么调用
pthread_cond_signal
将不会有任何效果。
代码
条件变量的使用
条件变量通常与互斥锁配套使用,线程持有互斥锁了,才可以去检查某个特定的条件是否满足,满足了才能去访问临界资源,如果不满足就去排队,所以条件变量一般在加锁和解锁之间使用,所以在排队的线程曾经拥有互斥锁!
线程去排队时,需要把曾经申请到的互斥锁释放掉,让其他线程去申请锁,线程不可以拿着锁去排队,否则无法达到同步,无法保持良好的并发性!所以 pthread_cond_wait 函数的参数还包括指向已经加锁的互斥锁的指针,就是为了把线程曾经申请到的互斥锁释放掉!
唤醒一个线程:
#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
pthread_cond_t gcond=PTHREAD_COND_INITIALIZER;//条件变量
pthread_mutex_t gmutex=PTHREAD_MUTEX_INITIALIZER;//互斥锁
void* SlaverCore(void* args)
{
std::string name=static_cast<const char*>(args);
//被唤醒
while(true)
{
pthread_mutex_lock(&gmutex);//加锁
//一般条件变量在加锁与解锁之间使用
pthread_cond_wait(&gcond,&gmutex);
//等待成功,被唤醒了
std::cout<<"当前被唤醒的线程为:"<<name<<std::endl;
pthread_mutex_unlock(&gmutex);//解锁
}
}
void* MasterCore(void* args)
{
sleep(3);
std::cout<<"master 开始工作..."<<std::endl;
//唤醒
while(true)
{
pthread_cond_signal(&gcond);
std::cout<<"master 唤醒线程..."<<std::endl;
sleep(1);
}
}
//创建一批被控制的线程
void StartSlaver(std::vector<pthread_t> *tidsptr,int num=3)
{
for(int i=0;i<num;i++)
{
pthread_t tid;
char *name=new char[64];
snprintf(name,64,"slaver-%d",i+1);
int n=pthread_create(&tid,nullptr,SlaverCore,name);
if(n==0)
{
std::cout<<"create success: "<<name<<std::endl;
tidsptr->emplace_back(tid);
}
}
}
//创建主线程
void StartMaster(std::vector<pthread_t> *tidsptr)
{
pthread_t tid;
int n=pthread_create(&tid,nullptr,MasterCore,nullptr);
if(n==0)//创建成功
{
std::cout<<"create master success"<<std::endl;
}
tidsptr->emplace_back(tid);
}
void WaitThread(std::vector<pthread_t> tids)
{
for(auto &tid:tids)
{
pthread_join(tid,nullptr);//等待线程
}
}
int main()
{
std::vector<pthread_t> tids;//存线程id
StartMaster(&tids);
StartSlaver(&tids,5);
WaitThread(tids);
return 0;
}
广播唤醒线程:
#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
pthread_cond_t gcond=PTHREAD_COND_INITIALIZER;//条件变量
pthread_mutex_t gmutex=PTHREAD_MUTEX_INITIALIZER;//互斥锁
void* SlaverCore(void* args)
{
std::string name=static_cast<const char*>(args);
//被唤醒
while(true)
{
pthread_mutex_lock(&gmutex);//加锁
//一般条件变量在加锁与解锁之间使用
pthread_cond_wait(&gcond,&gmutex);
//等待成功,被唤醒了
std::cout<<"当前被唤醒的线程为:"<<name<<std::endl;
pthread_mutex_unlock(&gmutex);//解锁
}
}
void* MasterCore(void* args)
{
sleep(3);
std::cout<<"master 开始工作..."<<std::endl;
//唤醒
while(true)
{
pthread_cond_broadcast(&gcond);
std::cout<<"master 唤醒线程..."<<std::endl;
sleep(1);
}
}
//创建一批被控制的线程
void StartSlaver(std::vector<pthread_t> *tidsptr,int num=3)
{
for(int i=0;i<num;i++)
{
pthread_t tid;
char *name=new char[64];
snprintf(name,64,"slaver-%d",i+1);
int n=pthread_create(&tid,nullptr,SlaverCore,name);
if(n==0)
{
std::cout<<"create success: "<<name<<std::endl;
tidsptr->emplace_back(tid);
}
}
}
//创建主线程
void StartMaster(std::vector<pthread_t> *tidsptr)
{
pthread_t tid;
int n=pthread_create(&tid,nullptr,MasterCore,nullptr);
if(n==0)//创建成功
{
std::cout<<"create master success"<<std::endl;
}
tidsptr->emplace_back(tid);
}
void WaitThread(std::vector<pthread_t> tids)
{
for(auto &tid:tids)
{
pthread_join(tid,nullptr);//等待线程
}
}
int main()
{
std::vector<pthread_t> tids;//存线程id
StartMaster(&tids);
StartSlaver(&tids,5);
WaitThread(tids);
return 0;
}