【问题标题】:Must a call to pthread_cond_wait be woken up by pthread_cond_signal?对 pthread_cond_wait 的调用必须被 pthread_cond_signal 唤醒吗?
【发布时间】:2012-10-27 16:56:13
【问题描述】:

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

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

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

【问题讨论】:

    标签: c pthreads


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

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

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

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      Thw pthread_cond_wait 调用将在 a) 条件变量发出信号和 b) 另一个线程解锁互斥锁后解除阻塞。然后pthread_cond_wait 将锁定互斥体并返回。

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

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

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

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

          【解决方案4】:

          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)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-11
            • 1970-01-01
            • 1970-01-01
            • 2022-01-13
            • 2012-01-25
            • 1970-01-01
            • 2013-05-07
            相关资源
            最近更新 更多