pthread_cond_signal

pthread_cond_broadcast, pthread_cond_signal - broadcast or signal a condition

语法

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond); 

描述

这2个函数会解除在某个条件变量上阻塞的线程的阻塞。

pthread_cond_broadcast()会解除当前所有在某个条件变量上阻塞的线程的阻塞。

pthread_cond_signal()至少解除一个某个条件变量上阻塞的线程的阻塞,如果有任何线程阻塞在条件变量上的话。

如果超过一个以上的线程阻塞在一个条件变量上,调度策略决定了被解除阻塞的线程的顺序。当调用了pthread_cond_broadcast()或者pthread_cond_signal()唤醒线程后,被唤醒的线程要想从pthread_cond_wait()或者pthread_cond_timedwait()返回,首先得获取pthread_cond_wait()或者pthread_cond_timedwait()的互斥锁。解除阻塞的线程会根据调度策略来竞争互斥锁,如同每个都调用了pthread_mutex_lock()。

线程在调用pthread_cond_broadcast()或pthread_cond_signal()函数时,无论它当前是否拥有调用pthread_cond_wait()或pthread_cond_timedwait()的线程在等待期间与条件变量相关联的互斥锁; 但是,如果需要可预测的调度行为,调用pthread_cond_broadcast()或pthread_cond_signal()的线程应该使用互斥锁锁定。

如果当前没有线程阻塞在条件变量上,那么pthread_cond_broadcast()或pthread_cond_signal()函数不应该生成任何效果。

如果成功,pthread_cond_broadcast()和pthread_cond_signal()函数将返回零; 否则,应返回错误编号以指示错误。

(ps:唤醒线程和解除线程阻塞,在本文中是同一个意思)

应用案例

当共享变量的状态改变之后,多个线程能够继续处理它的任务,这类问题就会使用到pthread_cond_broadcast()函数。比如单个生产者/多个消费者问题,假设生产者可以在列表上插入多个项目,消费者一次访问一个项目。 通过调用pthread_cond_broadcast()函数,生产者将通知正在等待的所有消费者,从而应用程序将在多核处理器上获得更多的吞吐量。 此外,pthread_cond_broadcast()可以更轻松地实现读写锁。 当写线程释放锁时,可以通过pthread_cond_broadcast()函数来唤醒所有正在等待的读线程。 最后,两阶段提交协议可以使用此广播函数来通知所有客户端即将发生的事务提交。

条件变量的多唤醒问题

在多核处理器上,希望使用pthread_cond_signal()来只唤醒一个阻塞在条件变量的线程,是不可能实现的。考虑pthread_cond_wait()和pthread_cond_signal()的以下部分实现,由给定顺序的两个线程执行。 一个线程试图等待条件变量,另一个线程同时执行pthread_cond_signal(),而第三个线程已经在等待。

pthread_cond_wait(mutex, cond):
    value = cond->value; /* 1 */
    pthread_mutex_unlock(mutex); /* 2 */
    pthread_mutex_lock(cond->mutex); /* 10 */
    if (value == cond->value) { /* 11 */
        me->next_cond = cond->waiter;
        cond->waiter = me;
        pthread_mutex_unlock(cond->mutex);
        unable_to_run(me);
    } else
        pthread_mutex_unlock(cond->mutex); /* 12 */
    pthread_mutex_lock(mutex); /* 13 */

pthread_cond_signal(cond):
    pthread_mutex_lock(cond->mutex); /* 3 */
    cond->value++; /* 4 */
    if (cond->waiter) { /* 5 */
        sleeper = cond->waiter; /* 6 */
        cond->waiter = sleeper->next_cond; /* 7 */
        able_to_run(sleeper); /* 8 */
    }
    pthread_mutex_unlock(cond->mutex); /* 9 */

结果是,由于对pthread_cond_signal()的一次调用,多个线程可以从其对pthread_cond_wait()或pthread_cond_timedwait()的调用返回。 这种效应称为“虚假唤醒”。 注意,情况是自我纠正的,因为被唤醒的线程数是有限的。但是纠正此问题将降低并发的性能。

允许虚假唤醒的另一个好处是应用程序被迫在条件等待周围编写谓词测试循环。 这也使得应用程序可以容忍在应用程序的某些其他部分中编码的相同条件变量上的多余条件广播或信号。 由此产生的应用程序更加健壮。 因此,IEEE Std 1003.1-2001明确记录了可能发生虚假唤醒的情况。

参考:pthread_cond_signal(3) - Linux man page

猜你喜欢

转载自blog.csdn.net/qq_26222859/article/details/81076609