不同线程之间的互斥锁同步

时间:2018-07-24 09:54:44

标签: c++ multithreading mutex condition-variable boost-mutex

由于我最近开始编码多线程程序,所以这可能是一个愚蠢的问题。我发现了很棒的互斥和条件变量用法。据我所知,使用范围是:

  1. 保护代码段/共享资源不受多线程访问的破坏。因此,锁定该部分,从而可以控制将访问哪个线程。
  2. 如果一个线程正在等待来自另一个线程的资源/条件,则可以使用cond.wait()而不是每毫秒进行轮询

现在考虑下面的类示例:

class Queue {
private:
    std::queue<std::string> m_queue; 
    boost::mutex m_mutex; 
    boost::condition_variable m_cond; 
    bool m_exit;

public:
    Queue()
    : m_queue() 
    , m_mutex() 
    , m_cond()
    , m_exit(false) 
    {}

    void Enqueue(const std::string& Req) 
    { 
        boost::mutex::scoped_lock lock(m_mutex);
        m_queue.push(Req);
        m_cond.notify_all();
    }

    std::string Dequeue() 
    { 
        boost::mutex::scoped_lock lock(m_mutex);
        while(m_queue.empty() && !m_exit) 
        {      
            m_cond.wait(lock);     
        }

        if (m_queue.empty() && m_exit) return "";

        std::string val = m_queue.front(); 
        m_queue.pop(); 
        return val; 
    }

    void Exit()
    {
        boost::mutex::scoped_lock lock(m_mutex);
        m_exit = true;
        m_cond.notify_all();
    }
} 

在上面的示例中,可以调用Exit(),它将通知在Dequeue上等待的线程该退出了,而无需等待队列中的更多数据。 我的问题是,由于出队已经获得了锁(m_mutex),Exit如何获得相同的锁(m_mutex)?除非出队释放了锁,然后只有Exit才能获取它?

我在Destructor实现中也看到了这种模式,使用相同的类成员互斥量,Destructor通知所有线程(类方法)该终止它们各自的循环/函数了。

1 个答案:

答案 0 :(得分:2)

As Jarod mentions in the comments, the call

m_cond.wait(lock)

is guaranteed to atomically unlock the mutex, releasing it for the thread, and starts listening to notifications of the condition variable (see e.g. here). This atomicity also ensures any code in the thread is executed after the listening is set up (so no notify calls will be missed). This assumes of course that the thread first locks the mutex, otherwise all bets are off.

Another important bit to understand is that condition variables may suffer from "spurious wakeups", so it is important to have a second boolean condition (e.g. here, you could check the emptiness of your queue) so that you don't end up awoken with an empty queue. Something like this:

m_cond.wait(lock, [this]() { return !m_queue.empty() || m_exit; });