2017-06-15 64 views
1

我正试图解决C++ 11中的生产者消费者问题。 我有一个保存资源的对象,多个线程可以添加或使用这些资源。我的问题是,当我尝试在该对象上实现 a“当可用时使用”方法。 请假设插入/删除操作的复杂性很小。如何在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中解决这个问题?

+0

可能与您的问题无关,但请使用'std :: unique_lock'等锁定锁来锁定/解锁互斥锁。 –

+0

锁是一个unique_lock –

+0

什么是'wmux'?你应该锁定'lock',并执行'bell.wait(lock)',以避免竞争状态。 – erenon

回答

1
lock.unlock(); //problem line 
    bell.wait(wmux); //waits until addResource rings the bell 

是的,这确实是问题线。

要正确使用作为设计条件变量,你解锁wait()之前互斥荷兰国际集团在其相关的条件变量。 wait()在一个条件变量上,在等待期间原子地解锁它,并且一旦线程已经被执行,重新获得互斥锁。解锁和等待以及被通知并锁定后的唤醒都是原子操作。

所有notify() s应该在锁定互斥锁时发出。所有wait()也是在互斥锁完全锁定的情况下完成的。正如我所提到的那样,notify()是原子的,这导致所有与互斥体相关的操作都是原子性的和完全排序的,包括管理受互斥体保护的资源,以及通过条件变量进行线程通知,现在也受互斥体保护。

有一些设计模式可以在不使用互斥保护的情况下通知条件变量。但是它们更难以正确实现并且仍然实现线程安全语义。所有的条件变量操作也受互斥锁保护,除了互斥锁保护的所有其他功能外,实现起来也更加简单。

+0

谢谢你的回答。 因此,如果我理解正确,在addResource中,应该在解锁互斥体之前调用bell.notify_one()。 另外,在getResource中,我应该删除lock.unlock(),并使用bell.wait(lock)而不是bell.wait(wmux); 我说得对吗? –

+0

这听起来正确;当然这取决于实际的实现细节,因为你只是在你的问题中总结了你的代码。注意[wait()]的要求(http://en.cppreference.com/w/cpp/thread/condition_variable/wait):“lock - std :: unique_lock 类型的对象,其中***必须是被当前线程锁定***“。 –

1

std::condition_variable::wait需要在您的互斥锁上传递锁定的std::unique_lockwait将作为其操作的一部分解锁互斥锁,并在它返回之前重新锁定它。

使用类似std::lock_guardstd::unique_lock的锁保护装置的正常方式是在本地构建它们,并让它们的构造函数锁定您的互斥锁,并将它们的析构函数解锁。

此外,您可以通过提供std::condition_variable::wait的谓词来避免原始代码中的外部while循环。

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 
    } 
}; 
+0

谢谢你的时间,你的回答很好地描述了另一个人以前已经回答过的内容。可惜他删除了他的帖子:S –