c - 为什么 pthreads 的条件变量函数需要互斥量?

标签 c pthreads mutex condition-variable

我正在阅读 pthread.h;条件变量相关函数(如 pthread_cond_wait(3))需要一个互斥体作为参数。为什么?据我所知,我将创建一个互斥体 just 用作该参数?该互斥量应该做什么?

最佳答案

这只是(或最初)实现条件变量的方式。

互斥量用于保护条件变量本身。这就是为什么您需要在等待之前将其锁定。

等待将“自动”解锁互斥体,允许其他人访问条件变量(用于信号)。然后,当条件变量被通知或广播时,等待列表中的一个或多个线程将被唤醒,并且该线程的互斥锁将再次神奇地锁定。

您通常会看到以下使用条件变量的操作,说明它们的工作原理。以下示例是一个工作线程,它通过向条件变量发送信号来工作。

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            do the work.
    unlock mutex.
    clean up.
    exit thread.

如果等待返回时有一些可用,则工作在此循环内完成。当线程被标记为停止工作时(通常由另一个线程设置退出条件,然后启动条件变量以唤醒该线程),循环将退出,互斥量将被解锁,该线程将退出。

上面的代码是单消费者模型,因为互斥体在工作完成时保持锁定状态。对于多消费者变体,您可以使用,作为示例:

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            copy work to thread local storage.
            unlock mutex.
            do the work.
            lock mutex.
    unlock mutex.
    clean up.
    exit thread.

这允许其他消费者在这个消费者工作时接收工作。

条件变量减轻了您轮询某些条件的负担,而不是让另一个线程在需要发生某些事情时通知您。另一个线程可以告诉该线程工作可用,如下所示:

lock mutex.
flag work as available.
signal condition variable.
unlock mutex.

绝大多数经常被错误地称为虚假唤醒的事情通常总是因为多个线程在它们的 pthread_cond_wait 调用(广播)中发出信号,一个会返回互斥锁,执行工作,然后重新等待。

然后当没有工作要做时,第二个信号线程可以出来。因此,您必须有一个额外的变量来指示应该完成工作(这在本质上是受 condvar/mutex 对的互斥保护的 - 但是其他线程需要在更改互斥之前锁定它)。

从技术上讲,线程从条件等待中返回而不被另一个进程踢出是可能的(这是一个真正的虚假唤醒)但是,在我多年的 pthreads 工作中,无论是在代码的开发/服务以及作为它们的用户,我从未收到过其中之一。也许那只是因为惠普有一个不错的实现:-)

无论如何,处理错误情况的相同代码也处理了真正的虚假唤醒,因为不会为这些设置工作可用标志。

关于c - 为什么 pthreads 的条件变量函数需要互斥量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2763714/

相关文章:

c# - C# 中的 typedef void* 替代方案

c++ - 混合 C 和 C++ 代码的静态分析怪异符号行为

c - 错误 : not a member of structure or union, 导致内存泄漏

c - Pthread I/O 缓冲区 : Why doese it print a redundant line?

c# - Solaris 上的单声道编译

c - 读者-作家问题作家偏好(读者可能会挨饿)

c# - 奇数按降序排列,偶数按升序排列

c++ - 将指针传递给声明为友元的类成员的 pthread

c - 为什么我的 pthread 函数没有打印出我所期望的结果?

c++ - std::mutex 是否足以使所有先前的读写发生在同一线程中的后续读写之前?