POSIX线程:条件变量

来源:互联网 发布:hystrix 源码下载 编辑:程序博客网 时间:2024/05/16 10:59

条件变量是 POSIX 线程结构,可以让您在遇到某些条件时“唤醒”线程。可以将它们看作是一种线程安全的信号发送。

如果线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值。但这是在浪费时间和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用 pthread_cond_wait() 调用来等待特殊条件发生。了解 pthread_cond_wait() 的作用非常重要;它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。

 

首先,让我们考虑以下情况:线程为查看链表而锁定了互斥对象,然而该链表恰巧是空的。这一特定线程什么也干不了:其设计意图是从链表中除去节点,但是现在却没有节点。因此,它只能:

锁定互斥对象时,线程将调用 pthread_cond_wait(&cond, &mutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改链表),并等待条件 cond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改链表,可能还会添加项。

此 时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件cond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。

现在继续说明,假设另一个线程(称作“2 号线程”)锁定了mutex 并对链表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&cond)。此操作之后,2 号线程将使所有等待 cond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。

 

现 在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&cond) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样的!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查链表,查看它所感兴趣的更改。

 

整个过程执行情况如下:

 

第一个线程首先调用:

 

然后,它检查了链表。没有找到感兴趣的东西,于是它调用:

 

然后,pthread_cond_wait() 调用在返回前执行许多操作:

 

 

 

它对mutex 解锁,然后进入睡眠状态,等待 cond 以接收 POSIX 线程“信号”。一旦接收到“信号”(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回,它还要做一件事:重新锁定 mutex:

      
pthread_cond_wait() 知道我们在查找 mutex “背后”的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。

 

记住:在调用 pthread_cond_wait() 之前,pthread_cond_wait() 调用返回之后,互斥对象都是处于锁定状态。

 

条件变量是一个需要初始化的真实数据结构。以下就是初始化的方法。首先,定义或分配一个条件变量,如下所示:

     

然后,调用以下函数进行初始化:

在释放或废弃条件变量之前,需要毁坏它,如下所示:

 

 

一旦初始化了互斥对象和条件变量,就可以等待某个条件,如下所示:

 

请注意,代码在逻辑上应该包含 cond 和 mutex。一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据“内部”的一种特殊的条件更改。一个互斥对象可以用于许多条件变量(例 如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。

 

 

 

对于发送信号和广播,需要注意一点。如果线程更改某些共享数据,而且它想要唤醒所有正在等待的线程,则应使用 pthread_cond_broadcast 调用,如下所示:

 

在某些情况下,活动线程只需要唤醒第一个正在睡眠的线程。假设您只对队列添加了一个工作作业。那么只需要唤醒一个工作程序线程(再唤醒其它线程是不礼貌的!):

    

此函数只唤醒一个线程。如果 POSIX 线程标准允许指定一个整数,可以让您唤醒一定数量的正在睡眠的线程,那就更完美了。

 

 

多线程工作组

在这个方案中,我们创建了许多工作程序线程。每个线程都会检查 wq(“工作队列”),查看是否有需要完成的工作。如果有需要完成的工作,那么线程将从队列中除去一个节点,执行这些特定工作,然后等待新的工作到达。

与此同时,主线程负责创建这些工作程序线程、将工作添加到队列,然后在它退出时收集所有工作程序线程。

需要队列是出于两个原因。首先,需要队列来保存工作作业。还需要可用于跟踪已终止线程的数据结构。

 

 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 狗长了个肿瘤怎么办 腺肌瘤糖类抗原125升高怎么办 糖类抗原724单项升高怎么办? 化疗期间糖类抗原升高怎么办? 门面租客到期不搬怎么办 这几天老想初恋怎么办 结婚了还想初恋怎么办 九年了想初恋了怎么办 吃肥肉恶心想吐怎么办 宝宝吃了母乳不吃奶粉怎么办 母猫的奶少怎么办 鲤鱼打挺起不来怎么办 练不会鲤鱼打挺怎么办 鲤鱼打挺脖子痛怎么办 新买的沙发太高怎么办 额最后离开公司没人关灯怎么办 看到我妹妹就烦怎么办 野塘钓鱼不开口怎么办 团关系找不到接收地怎么办 两岁宝宝就是不肯说话怎么办 两岁宝宝不肯吃药怎么办 并蹄莲叶子大黄怎么办 异地恋见面来大姨妈怎么办 奶水太多宝宝老是呛到怎么办 奶水太多吃奶婴儿呛怎么办 人家不愿意交我为朋友怎么办 面对诋毁我的人怎么办 做事太细致速度太慢怎么办 高一儿子早恋了怎么办 儿子18岁谈朋友怎么办 和朋友为钱吵架怎么办 感情里总担心失去怎么办 眼石移出盲僧怎么办 打仗把小便踢肿了怎么办 腿上都是挠的疤怎么办 脚上的肉烂了怎么办 商铺门口有电杆怎么办 漏电保护器坏了怎么办 空开进线烧了怎么办 过压保护灯亮怎么办 美的热水器接地异常怎么办