[英]Does it make sense to use different mutexes with the same condition variable?
cppreference.com条件变量的 notify_one() function 的文档说明如下
通知线程不需要持有与等待线程持有的互斥量相同的锁; 事实上这样做是悲观的,因为被通知的线程会立即再次阻塞,等待通知线程释放锁。
这句话的第一部分很奇怪,如果我在通知线程和被通知线程中持有不同的互斥量,那么互斥量就没有真正的意义,因为这里没有“阻塞”操作。 事实上,如果持有不同的互斥锁,那么虚假唤醒可能导致通知丢失的可能性是有可能的! 我的印象是在这种情况下我们最好不要锁定通知线程。 有人可以澄清一下吗?
以 cppreference 页面中关于条件变量的以下内容为例。
std::mutex m; // this is supposed to be a pessimization
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m); // a different, local std::mutex is supposedly better
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m); // a different, local std::mutex is supposedly better
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
}
附言。 我看到了一些标题相似的问题,但它们指的是问题的不同方面。
通知线程不需要持有与等待线程持有的互斥量相同的锁;
那是误导。 问题在于“相同”这个词。 他们应该说,“......不需要持有任何互斥量的锁......”这才是真正的重点。 等待线程在进入wait()
调用时应该锁定互斥锁的一个重要原因是:它正在等待某个共享数据结构中的某些更改,并且它需要在访问该结构时锁定互斥锁以检查是否等待的变化实际上已经发生了。
notify()ing
线程可能需要锁定同一个互斥锁以实现该更改,但程序的正确性不取决于它是在释放互斥锁之前还是之后调用notify()
。
我觉得这里cppreference的措辞有些别扭。 我认为他们只是试图将与条件变量一起使用的互斥锁与其他不相关的互斥锁区分开来。
使用具有不同互斥量的条件变量是没有意义的。 互斥锁用于对实际语义条件(在示例中它只是变量ready
)进行任何原子更改,因此必须在更新或检查条件时保持它。 还需要确保未阻塞的等待线程可以立即检查条件,而不会再次遇到竞争条件。
我的理解如下:没关系,在调用notify_one
时不锁定与条件变量关联的互斥锁,或者根本不锁定任何互斥锁,但是出于不同原因持有其他互斥锁是可以的。
悲观不是只使用一个互斥锁,而是当您知道另一个线程应该在收到通知后立即尝试获取互斥锁时,持有这个互斥锁的时间超过必要的时间。
我认为我的解释与cppreference on condition variable中给出的解释一致:
打算修改共享变量的线程必须
获得一个
std::mutex
(通常通过 std::lock_guard)在持有锁的情况下执行修改
在
std::condition_variable
上执行notify_one
或notify_all
(通知时不需要持有锁)
即使共享变量是原子的,也必须在互斥量下修改它才能将修改正确发布到等待线程。
任何打算等待
std::condition_variable
的线程都必须在用于保护共享变量的同一个互斥锁上获取一个std::unique_lock<std::mutex>
此外,标准明确禁止对wait
、 wait_for
或wait_until
使用不同的互斥量:
lock.mutex() 为所有并发等待(通过 wait、wait_for 或 wait_until)线程提供的每个锁 arguments 返回相同的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.