我试图解决C ++ 11中的生产者消费者问题。 我有一个保存资源的对象,多个线程可以 添加或使用这些资源。我的问题是当我尝试实施时 a"在可用时消费"该对象的方法。 请假设插入/删除操作具有微不足道的复杂性。
对代码中逻辑的一点解释。
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解决这个问题?
答案 0 :(得分:1)
lock.unlock(); //problem line
bell.wait(wmux); //waits until addResource rings the bell
是的,这确实是问题所在。
要正确使用条件变量,请在wait()
相关条件变量之前执行 不 解锁互斥锁。 wait()
条件变量在等待期间以原子方式解锁它,并在线程notify()
编辑后重新获取互斥锁。解锁和等待以及唤醒后被通知和锁定都是原子操作。
应该在互斥锁被锁定时发出所有notify()
个。互斥锁完全锁定时,所有wait()
也会完成。正如我所提到的,notify()
是原子的,这导致所有与互斥锁相关的操作都是原子的并完全排序,包括管理互斥锁保护的资源,以及通过条件变量进行线程通知,现在受到条件变量的保护也是一个互斥体。
可以实现的设计模式可以在不使用互斥锁保护的情况下通知条件变量。但是他们更难以正确实现并仍然实现线程安全的语义。除了互斥锁保护的所有其他内容之外,所有条件变量操作也受互斥锁保护,实现起来要简单得多。
答案 1 :(得分:1)
std::condition_variable::wait
需要在您的互斥锁上传递锁定的std::unique_lock
。 wait
将解锁互斥锁作为其操作的一部分,并在返回之前重新锁定它。
使用std::lock_guard
和std::unique_lock
等锁定守卫的正常方法是在本地构建它们,让它们的构造函数锁定你的互斥锁,并让它们的析构函数解锁它。
此外,您可以通过向while
提供谓词来避免原始代码中的外部std::condition_variable::wait
循环。
struct ResourceManager {
std::mutex mux;
std::condition_variable bell;
void addResource(T resource)
{
std::lock_guard<std::mutex> lock{mux};
// Add the resource
bell.notify_one();
}
T getResource()
{
std::unique_lock<std::mutex> lock{mux};
bell.wait(lock, [this](){ return resourceIsAvailable(); });
return // the ressource
}
};