我正在尝试解决 C++11 中的生产者消费者问题。 我有一个保存资源的对象,并且多个线程可以 添加或消耗这些资源。我的问题是当我尝试实现时 该对象上的“可用时使用”方法。 请假设插入/删除操作的复杂性微乎其微。
对代码中逻辑的一些解释。
struct ResourceManager{
std::mutex mux;
std::unique_lock lock{mux};
std::condition_variable bell;
void addResource(/*some Resource*/){
lock.lock();
//add resource
lock.unlock();
bell.notify_one(); //notifies waiting consumer threads to consume
}
T getResource(){
while(true){
lock.lock();
if(/*resource is available*/){
//remove resource from the object
lock.unlock();
return resource;
}else{
//new unique lock mutex object wmux creation
lock.unlock(); //problem line
bell.wait(wmux); //waits until addResource rings the bell
continue;
}
}
}
};
假设以下场景:
-两个线程T1、T2几乎同时调用addResource、getResource。
-T2 锁定互斥体,并发现没有更多可用资源,
因此它必须阻塞,直到有新资源可用。
因此它会解锁互斥体并设置等待铃声。
-T1 运行匹配速度更快。当互斥体解锁时,
它立即添加资源,并在 T2 设置等待铃之前,
T1已经按响了铃声,但没有通知任何人。
-T2无限期地等待铃声响起,但不会添加更多资源。
我假设锁定互斥体的线程可能是唯一的一个 来解锁它。因此,如果我尝试在解锁互斥体之前调用bell.wait, 互斥锁永远无法解锁。
如果可能的话,我想使用无时间等待或多次检查的解决方案。
那么在 C++11 中我可以通过什么方式解决这个问题呢?
最佳答案
lock.unlock(); //problem line
bell.wait(wmux); //waits until addResource rings the bell
是的,这确实是问题所在。
要按照设计正确使用条件变量,您不要在wait()
相关条件变量之前解锁互斥体。对条件变量进行 wait()
操作会在等待期间自动解锁该变量,并在线程被 notify()
处理后重新获取互斥锁。解锁并等待和通知后唤醒并锁定都是原子操作。
所有notify()
都应该在互斥锁被锁定时发出。所有 wait()
也会在互斥体完全锁定时完成。正如我提到的,notify() 是原子的,这会导致所有与互斥体相关的操作都是原子的且完全排序,包括管理受互斥体保护的资源,以及通过条件变量进行线程通知,现在也受到互斥锁的保护。
可以实现一些设计模式来通知条件变量而不使用互斥锁保护。但它们更难正确实现并仍然实现线程安全语义。除了互斥体保护的所有其他内容之外,让所有条件变量操作也受到互斥体的保护,实现起来要简单得多。
关于c++ - 如何正确解决C++11中的生产者消费者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44577856/