0

我了解如何使用条件变量(这个构造的糟糕名称,IMO,因为 cv 对象既不是变量也不是表示条件)。所以我有一对线程,用 Boost.Thread规范地设置为:

bool awake = false;
boost::mutex sync;
boost::condition_variable cv;
void thread1()
{
    boost::unique_lock<boost::mutex> lock1(sync);
    while (!awake)
        cv.wait(lock1);
    lock1.unlock();    // this line actually not canonical, but why not?
    // proceed...
}
void thread2()
{
    //...
    boost::unique_lock<boost::mutex> lock2;
    awake = true;
    lock2.unlock();
    cv.notify_all();
}

我的问题是:thread2 真的需要保护对 的分配awake吗?在我看来,这个notify_all()电话应该就足够了。如果被操作和检查的数据不仅仅是一个简单的“可以继续”标志,我会看到互斥锁中的值,但在这里看起来有点过头了。

第二个问题是代码片段中提出的问题:为什么 Boost 文档没有显示线程 1 中的锁在“处理数据”步骤之前被解锁?

编辑:也许我的问题真的是:有没有比简历更干净的结构来实现这种等待?

4

2 回答 2

1

thread2 真的需要保护分配唤醒吗?

是的。从一个线程修改对象并在不同步的情况下从另一个线程访问它会产生未定义的行为。哪怕只是一个bool

例如,在某些多处理器系统上,写入可能只影响本地内存;如果没有显式的同步操作,其他线程可能永远不会看到更改。

为什么 Boost 文档没有显示 thread1 中的锁在“处理数据”步骤之前被解锁?

如果您在清除标志之前解锁了互斥锁,那么您可能会错过另一个信号。

是否有比 CV 更简洁的结构来实现这种等待?

在 Boost 和标准 C++ 库中,没有;条件变量足够灵活,可以处理任意共享状态,并且对于这种简单的情况不会特别复杂,因此不需要任何更简单的东西。

更一般地说,您可以使用信号量或管道在线程之间发送简单的信号。

于 2012-07-18T18:04:43.090 回答
1

形式上,你肯定需要两个线程中的锁:如果任何线程修改了一个对象,并且有多个线程访问它,那么所有 访问都必须同步。

在实践中,您可能会在没有锁的情况下侥幸逃脱;几乎可以肯定,notify_all它将发出必要的栅栏或 membar 指令,以确保内存正确同步。但是为什么要冒险呢?

As to the absense of the unlock, that's the whole point of the scoped locking pattern: the unlock is in the destructor of the object, so that the mutex will be unlocked even if an exception passes through.

于 2012-07-18T18:15:17.623 回答