2

标题基本上不言自明。如果我使用特定条件和互斥锁调用 pthread_cond_wait,该线程是否会一直阻塞,直到使用相应条件调用 pthread_cond_signal?或者,无论互斥体随后是否再次解锁,它都会解除阻塞?

如果答案是前者,我有后续。我有一个用于在我的线程之间传递消息的队列。我想确保一次只有一个线程可以将一个项目附加到队列中(因此使用互斥锁)。没有任何线程知道是否有任何其他线程将等待自己控制互斥锁。

当尝试将项目附加到队列时,我锁定互斥锁,等待队列未满并具有 pthread 条件,然后执行附加,然后解锁互斥锁。在解锁它之前,我是否应该执行一个 pthread_cond_signal,即使我不知道是否有任何其他线程会等待?如果多个线程在等待会发生什么?

4

4 回答 4

6
  1. 有人需要发信号/广播条件变量,否则等待线程不一定会被唤醒。您也可以获得“虚假”唤醒,但不要依赖它们。一旦服务员被唤醒(通过信号或广播或虚假的),它将开始尝试获取互斥锁,并且一旦它返回 pthread_cond_wait

  2. 如果有多个线程在等待,则其中一个被唤醒(任意或根据实现记录的规则)。在您描述的代码中,线程在此 condvar 上等待的唯一位置是当它尝试将项目添加到完整队列时。所以你有三个选择:

    • 每次从完整队列中删除元素时,都会向条件变量发出信号。这会唤醒 0 个或多个服务员之一。然后在它添加一个元素后再次发出信号,如果队列仍然没有满,以防万一有超过1个服务员。

    • 每次从完整队列中删除元素时广播条件变量。现在您不需要在添加元素后发出信号。

    • 每次删除元素时都会向条件变量发出信号,无论队列是否已满。如果您这样做,请不要犯错误,即将来更改代码以一次性删除两个元素并且只发出一次信号。每次您发出信号时,条件变量都会将服务员的数量减少一个,这种方法之所以有效,是因为它可以防止同时存在至少一个服务员,并且尝试获取互斥锁的被唤醒的前服务员少于队列中的空间。因此,当有可用空间时,您永远不会让线程等待。

想到这一点的方式是,您正在等待的“条件”是“队列未满”。每当条件变为真时,您应该发出信号或广播条件变量。如果您选择发信号,并且可能有多个服务员,那么每个服务员都需要发信号(以唤醒下一个服务员)如果条件在完成其操作后仍然为真。

如果您不关心一些额外上下文切换的性能成本,并且如果您已经编写了正确等待 condvar 以应对虚假唤醒的代码,那么在条件为时发出信号或广播 condvar 也是安全的还是假的。这是条件变量的优点之一,它可以更容易地推断代码的正确性。因此,“只要条件为真,您应该发出信号或广播条件变量”的要求仅意味着它所说的。当且仅当条件为真时,您不必发出信号。

因此,在这种情况下,每次从队列中删除元素时都可以发出信号,无论它是否事先已满。在这种情况下,很容易将信号的数量减少到必要的最小值,但有时它比确定条件是否为真更麻烦,所以如果它现在为真并且可能有,则改为发出信号以前是假的,或者甚至只是表明它现在可能是真的。我不建议无缘无故地增加额外的唤醒,但有时它会使代码更简单。

于 2012-11-07T16:05:30.227 回答
1

pthread_cond_wait一旦 a) 条件变量发出信号并且 b) 另一个线程已解锁互斥锁,thw调用将解除阻塞。然后pthread_cond_wait将锁定互斥锁并返回。

(请注意,您几乎总是需要在返回后重新检查您的真实测试,例如“队列是否已满?” 。通常出于这个原因,pthread_cond_wait您会在循环中调用它。)while

您无法使用 pthread API 检查是否有任何其他线程正在等待条件变量,因此正常模式是在解锁互斥锁之前pthread_cond_signalpthread_cond_broadcast之前调用。如果没有人在等待,这些都很便宜。

“信号”和“广播”的区别在于前者只唤醒一个等待线程(并没有指定哪个),而后者唤醒所有等待线程。

我认为在你的情况下,你应该在每次从队列中删除一个项目时“广播”,让插入线程知道他们有机会。然后,您不需要从其他插入发出插入线程的信号。

于 2012-11-07T16:07:08.553 回答
1

当您将项目放入队列时,如果队列先前为空,则向条件变量发出信号 - 这样您就可以唤醒可能等待从队列中获取项目的线程。如果队列不为空,则获取线程没有理由阻塞条件,因此不需要信号。

当您从队列中获取一个项目时,如果队列先前已完全满,则向另一个条件变量发出信号 - 这样您就可以唤醒一个可能等待将项目放入队列的线程。如果队列未满,则附加线程没有理由阻塞条件,因此不需要信号。

不要广播,除非您从队列中获取/放入多个项目。如果你只放一件物品,分别。只提供一个插槽并广播,您将唤醒所有线程,但只有一个线程能够取得进展,所有其他线程将返回等待并唤醒它们,它们恢复并返回睡眠将是纯粹的高架。

于 2012-11-07T16:22:05.663 回答
0

1 是的。与调用关联的互斥pthread_cond_wait()锁由函数解锁。

2 任何等待条件的线程都可以恢复。返回时pthread_cond_wait(),关联的互斥锁被锁定。

pthread_cond_wait

原子地解锁互斥锁(根据 pthread_unlock_mutex)并等待条件变量 cond 发出信号。线程执行被挂起并且在条件变量发出信号之前不会消耗任何 CPU 时间。调用线程必须在进入 pthread_cond_wait 时锁定互斥锁。在返回调用线程之前,pthread_cond_wait 重新获取互斥锁(根据 pthread_lock_mutex)。

于 2012-11-07T15:46:37.050 回答