前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程同步-条件变量

线程同步-条件变量

作者头像
南桥
发布2024-08-24 13:47:28
20
发布2024-08-24 13:47:28
举报
文章被收录于专栏:南桥谈编程

文章目录

  • 引言
  • 条件变量
    • 初始化条件变量:pthread_cond_init
    • 销毁条件变量:pthread_cond_destroy
    • 条件等待:pthread_cond_wait
    • 唤醒等待:pthread_cond_signal、pthread_cond_broadcast
    • 认识条件变量
    • 接口使用

引言

有一个非常好的VIP自习室,一次只允许一个人进来,每一个自习完成的同学归还钥匙后,不能立马申请,第二次申请必须排队,也就是说其他人也必须排队,这种去自习室自习有一定的顺序性,这称之为同步。

换言之,每一个线程在访问临界资源时,有一定的顺序性,这称之为线程的同步。这里的顺序性可以是严格的顺序性,也可以是宏观上的具有相对顺序性。

条件变量

一个条件变量是一个pthread_cond_t类型

初始化条件变量:pthread_cond_init

代码语言:javascript
复制
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);

cond:要初始化的条件变量 attrNULL

销毁条件变量:pthread_cond_destroy

代码语言:javascript
复制
int pthread_cond_destroy(pthread_cond_t *cond)

在调用 pthread_cond_destroy 之前,确保没有线程在等待这个条件变量

条件等待:pthread_cond_wait

代码语言:javascript
复制
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

cond:要在这个条件变量上等待 mutex:互斥量

唤醒等待:pthread_cond_signal、pthread_cond_broadcast

唤醒一个线程:

代码语言:javascript
复制
int pthread_cond_signal(pthread_cond_t *cond);

唤醒所有线程:

代码语言:javascript
复制
int pthread_cond_broadcast(pthread_cond_t *cond);

认识条件变量

有两个人分别是A和B,B往箱子中放苹果,A从箱子中拿苹果,A必须在B放完后拿苹果。箱子实际上是一个共享资源,苹果相当于数据,当B没有往共享资源中写数据时,A进行读取,那么读取的内容就是垃圾数据。A,B在进行操作的时候,必须保证箱子(共享资源)的安全,因此需要加锁pthread_mutex_lock ,完成之后进行解锁pthread_mutex_unlock。注定A,B需要互斥式的访问箱子(共享资源),这样才能保证B正在写的时候,A不会读取。A需要先检测箱子(共享资源)重是否有苹果(数据),当检测到没有苹果(数据),A需要解锁退出,然后A立马重新申请锁,再去检测,因此A可能在不断申请锁、解开锁。A如此频繁的申请锁,B可能抢不到锁。因此这种做法不合理:A、B互斥,如果B竞争能力弱的话,B抢不到锁,A只能频繁申请锁、解开锁。

为了解决这个矛盾,在上述场景中,引入一个铃铛,当A检测到箱子(共享资源)中没有苹果(数据),A解开锁之后不要再去申请锁,而是去铃铛那里等待铃铛,B放完苹果后摇铃铛,听到铃铛响了,A再去申请锁。

这里引入的铃铛就是条件变量,条件变量必须提供两个东西:

  1. 需要一个线程队列
  2. 需要有通知机制

此时又来一个C,也是来拿苹果,A和C就会形成竞争了,铃铛想起的时候,就会把A和C都唤醒,这就是pthread_cond_broadcast

接口使用

代码语言:javascript
复制
#include<iostream>
#include<string>
#include<unistd.h>
#include<pthread.h>

const int num=5;
pthread_mutex_t gmutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond=PTHREAD_COND_INITIALIZER;

void *Wait(void *args)
{
    std::string name=static_cast<const char*>(args);
    while (true)
    {
        pthread_mutex_lock(&gmutex);
        //条件等待
        pthread_cond_wait(&gcond,&gmutex);
        usleep(10000);
        std::cout<<"I am : "<<name<<std::endl;
        
        pthread_mutex_unlock(&gmutex);
        //usleep(100000);
    }
    
}

int main()
{
    pthread_t threads[num];
    for(int i=0;i<num;i++)
    {
        char *name=new char[1024];
        snprintf(name,1024,"thread-%d",i+1);
        pthread_create(threads+i,nullptr,Wait,(void *)name);
    }
    //唤醒线程
    while(true)
    {
        pthread_cond_signal(&gcond);   //一次唤醒一个线程
        //pthread_cond_broadcast(&gcond);   //一次唤醒多个线程
        std::cout<<"唤醒一个线程..."<<std::endl;
        sleep(2);
    }

    for(int i=0;i<num;i++)
    {
        pthread_join(threads[i],nullptr);
    }

    return 0;
}

为什么pthread_cond_wait在加锁和解锁之间使用?

确保条件检查的原子性:在多线程环境中,条件变量通常与互斥锁一起使用来保护共享资源。线程在检查条件之前需要持有锁,以避免其他线程修改共享资源。调用 pthread_cond_wait 时,函数会释放锁以让其他线程可以修改共享资源,然后在条件满足后重新获取锁,这样可以保证在条件变量被触发后,线程能够再次安全地检查条件和访问共享资源。 避免竞争条件:如果 pthread_cond_wait 不释放锁,那么其他线程将无法获取这个锁并修改条件,这可能导致死锁或线程无法继续工作。通过在 pthread_cond_wait 内部释放和重新获取锁,确保了条件检查的完整性和线程的正确同步。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
    • 引言
      • 条件变量
        • 初始化条件变量:pthread_cond_init
        • 销毁条件变量:pthread_cond_destroy
        • 条件等待:pthread_cond_wait
        • 唤醒等待:pthread_cond_signal、pthread_cond_broadcast
        • 认识条件变量
        • 接口使用
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档