16

我正在尝试了解条件变量。我想知道使用条件变量的常见情况是什么。

一个例子是在一个阻塞队列中,两个线程访问队列——生产者线程将一个项目推入队列,而消费者线程从队列中弹出一个项目。如果队列为空,则消费者线程一直在等待,直到生产者线程发送信号。

还有哪些其他需要使用条件变量的设计情况?

不过,我更喜欢基于经验的示例,例如真实应用程序中的示例。

4

5 回答 5

2

比消息队列更复杂的条件变量的一种用途是“共享锁”,其中不同的线程正在等待具有相同基本性质的细微不同的条件。例如,您有一个(非常简陋、简化的)网络缓存。缓存中的每个条目都有三种可能的状态:不存在、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而无需调度程序的任何帮助。我不能使用信号量或锁 per once_control,而只是在锁下进行初始化的原因是该函数不允许失败,并且once_control只有微不足道的初始化。就此而言,pthread_once它本身没有定义的错误代码,因此实现它可能会失败不会给你的调用者留下任何好的选择......

当然,对于这种模式,您必须小心缩放。每次任何初始化完成时,每个等待的线程都会唤醒以获取锁。因此,当您设计系统时,您会非常仔细地考虑分片,然后决定在看到已证明的性能问题之前,您不会费心做任何事情来实际实施它。

于 2010-03-19T11:32:15.970 回答
1

一个例子,除了你已经提到的消费者 - 生产者模型之外,还有屏障同步的使用。当线程进入屏障时,如果还有其他线程需要进入屏障,那么它们会等待一个条件变量。最后一个进入屏障的线程发出条件信号。

于 2010-03-19T10:28:09.383 回答
0

我用它来发送同步消息,其中添加了一个同步对象。
同步对象由一个带有“就绪”布尔值的条件变量组成。
在syncMsg::send() 函数中,有一个sync->wait(),在syncMsg::handle() 函数中,有一个sync->go()。

由于可能出现死锁,应谨慎使用。

于 2010-03-19T12:03:07.010 回答
0

我使用条件变量而不是容易出错的 Win32 事件对象。使用 condvars,您不必太担心虚假信号。等待多个事件发生也更容易。

Condvars 也可以代替信号量,因为它们更通用。

于 2010-03-19T12:54:56.917 回答
0

我知道这不是很有帮助,但是每当我希望线程等待某事发生或仅等到某事发生时,我都会使用条件变量。

我使用条件变量的一个非常常见的模式是一个后台线程,它每隔几分钟唤醒一次以进行一些处理,然后重新进入睡眠状态。在关闭主线程时通知后台线程完成,然后加入它完成。后台线程等待条件超时,以执行其睡眠。

后台线程遵循这个基本逻辑

void threadFunction() {
    initialisation();

    while(! shutdown()) {
        backgroundTask();

        shutdown_condition_wait(timeout_value);
    }

    cleanup();
}

这可以让后台线程迅速而优雅地关闭。

如果我有许多这样的线程,主函数会发出信号以关闭每个线程,然后在下一个之后加入每个线程。这使每个线程组件能够并行关闭。

于 2010-03-19T16:10:08.183 回答