【Linux】线程同步&条件变量

文章目录

  • 一. 为什么要线程同步
  • 二. 条件变量
    • 1. 条件变量的使用
    • 2. 简单使用
  • 结束语

一. 为什么要线程同步

通过互斥量,也就是加锁解锁,我们可以实现线程互斥,但是当访问的临界区代码较少时,线程执行会出现不停加锁解锁的情况。这样一会导致线程效率降低,二会导致其他线程无法访问到临界资源,从而出现饥饿问题
为了避免出现不断加锁解锁的情况,我们需要让使用完锁的线程,不能立刻重新申请锁,并且等待锁资源的其他线程,需要按照一定顺序进行等待。这就是线程同步

【Linux】线程同步&条件变量_第1张图片

二. 条件变量

1. 条件变量的使用

当线程互斥地访问某个变量时,它可能会发现,在其他线程改变状态前,它什么也做不了
例如一个队列要读取队列的数据,但是当他发现队列为空时,那么他只能等待,等待其他线程将数据添加到队列中。这种情况就需要用到条件变量

  • 同步: 在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,这就叫做同步
  • 竞争条件: 因为时序问题,而导致程序异常,我们称之为竞态条件

Linux中,条件变量是定义的一个联合体嵌套结构体,由操作系统帮我们维护
【Linux】线程同步&条件变量_第2张图片


条件变量初始化

条件变量有两种初始化方式

静态初始化

同互斥量(锁)一样,操作系统提供静态的条件变量

//静态初始化
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
#include
#include
#include

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;
}

【Linux】线程同步&条件变量_第3张图片

我们也可以一次性全部唤醒

	//唤醒
    while(true)
    {
        cout<<"主线程唤醒线程..."<<endl;
        //pthread_cond_signal(&cond);
        pthread_cond_broadcast(&cond);
        sleep(1);
    }

【Linux】线程同步&条件变量_第4张图片

结束语

感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

你可能感兴趣的:(Linux学习笔记,开发语言,c++,linux,运维)