我正在尝试了解条件变量。我想知道使用条件变量的常见情况。
一个例子是阻塞队列,其中两个线程访问队列 - 生产者线程将项目推入队列,而消费者线程从队列中弹出一个项目。如果队列为空,则消费者线程正在等待,直到生产者线程发送信号为止。
您需要使用条件变量的其他设计情况是什么?
我更喜欢基于经验的示例,例如真实应用程序中的示例。
答案 0 :(得分:2)
条件变量的一个用途是比一个消息队列更复杂,是“共享锁”,其中不同的线程正在等待具有相同基本性质的微妙不同条件。例如,您有一个(非常简单,简化)的Web缓存。缓存中的每个条目都有三种可能的状态:不存在,IN_PROGRESS,COMPLETE。
getURL:
lock the cache
three cases for the key:
not present:
add it (IN_PROGRESS)
release the lock
fetch the URL
take the lock
update to COMPLETE and store the data
broadcast the condition variable
goto COMPLETE
COMPLETE:
release the lock and return the data
IN_PROGRESS:
while (still IN_PROGRESS):
wait on the condition variable
goto COMPLETE
我实际上在没有调度程序帮助的情况下使用该模式来实现POSIX函数pthread_once
的变体。我无法使用每个once_control
的信号量或锁定,只是在锁定下进行初始化的原因是该函数不允许失败,once_control
只进行了简单的初始化。就此而言,pthread_once
本身没有定义的错误代码,因此将其实现为可能失败并不会给调用者带来任何好的选择......
当然,对于这种模式,你必须要小心缩放。每次完成任何初始化时,每个等待的线程都会唤醒以获取锁定。因此,当您设计系统时,您会非常仔细地考虑分片,然后在您看到经过验证的性能问题之前,决定不做任何事情来实际实施它。
答案 1 :(得分:1)
除了消费者 - 生产者模型之外,您已经提到的一个例子是barrier synchronization中的用法。当线程进入屏障时,如果还有其他线程需要进入屏障,那么它们会等待条件变量。进入屏障的最后一个线程表示该情况。
答案 2 :(得分:0)
我用它来发送同步消息,其中添加了一个sync-object 同步对象由一个带有“ready”布尔值的条件变量组成 在syncMsg :: send()函数中,有一个sync-> wait(),在syncMsg :: handle()函数中,有一个sync-> go()。
应该明智地使用,因为可能存在死锁。
答案 3 :(得分:0)
我使用条件变量而不是容易出错的Win32事件对象。使用condvars,您不必担心虚假信号。等待多个事件发生也更容易。
Condvars也可以替换信号量,因为它们更通用。
答案 4 :(得分:0)
我知道这不是很有帮助,但是每当我想要一个线程等待事情发生时,我就会使用条件变量,或者只是等到事情发生。
我使用条件变量的一个非常常见的模式是后台线程,它每隔几分钟就会唤醒一次,然后再进入休眠状态。在关闭时,主线程通知后台线程完成,然后加入完成。后台线程在超时条件下等待,以执行其休眠。
后台线程遵循这个基本逻辑
void threadFunction() {
initialisation();
while(! shutdown()) {
backgroundTask();
shutdown_condition_wait(timeout_value);
}
cleanup();
}
这样可以让后台线程快速,优雅地关闭。
如果我有许多这样的线程,主函数会发出每个关闭信号,然后在下一个之后加入每个线程。这使得每个线程组件可以并行关闭。