[译]为什么 pthread 条件变量(condition variable)函数要用到 mutex ?

原文是stackoverflow上的一篇问答,地址如下:
Why do pthreads’ condition variable functions require a mutex?

问题描述

我正在读pthread.h;跟条件变量(condition variable)有关的函数(如
pthread_cond_wait(3))需要一个互斥量(mutex)作为参数。为什么?就我看来,我建一
个互斥量就是为了当作参数用?这个互斥量的作用是什么?

paxdiablo 的回答

其实条件变量就是这样实现的。

互斥量是用来保护条件变量的。这就是你需要在做 wait 操作之前上锁的原因。

wait 操作会“自动”解锁 mutex,以使得其他线程可以使用条件变量(来发送信号)。当条
件变量收到信号或广播时,一个或多个在等待列表里的线程就会被唤醒,而互斥量将会神奇
地为那个线程上锁。

你一般会看到对条件变量进行如下操作来展示他们怎么工作。下面的例子是一个工作线程,
它借由环境变量收到信号的方式来接收工作。

1
2
3
4
5
6
7
8
9
10
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.

当有工作可以做时 wait 返回,然后在循环里进行处理。当线程被告知要停止工作时(一般
是由另一个线程设置退出变量,然后设置环境变量来唤醒该进程),循环退出,mutex 被解
锁然后线程退出。

以上代码是单个消费者模型,其中在工作被处理时互斥量仍然处于上锁状态。对于多消费者
模型来说,你可以用下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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.

这使得在该线程处理任务的同时其他消费者可以接受任务。

条件变量帮你免除了需要轮询某些状态的负担,取而代之的是让另一线程在需要进行工作时
通知你。那个进程可以用以下的方式通知有有工作可做:

1
2
3
4
lock mutex.
flag work as available.
signal condition variable.
unlock mutex.

大部分被错误地称作虚假唤醒的现象通常是因为多个线程在pthread_cond_wait中被信号
(或者广播)唤醒,但只有一个能对互斥量上锁,然后处理任务,最后再进入等待。

而其他被唤醒的线程发现没有工作可做。所以你不得不有一个额外的变量来表明有工作可以
做(这就是互斥量需要保护的,其他线程在改变它的值之前必须先把互斥量上锁)。

理论上,一个线程在没有被其他进程唤醒的情况下是有可能从 wait 状态中醒来的(这就是
纯粹的虚假唤醒),但在我做与 pthread 相关工作的这么多年里,无论是开发还是使用它,
我一次都没有遇到过这种情况。或许那只是因为 HP 有一个好的实现吧 :-)

无论怎么,能够处理错误情况的代码也能处理虚假唤醒的情况,因为工作可做标志不会因为
这些而被设置。

0%