0

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

  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 的线程该退出了,而无需等待队列中的更多数据。我的问题是,既然 Dequeue 已经获得了锁(m_mutex),那么 Exit 如何获得相同的锁(m_mutex)?除非 Dequeue 释放锁,否则只有 Exit 可以获取它吗?

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

4

1 回答 1

2

正如 Jarod 在评论中提到的那样,电话

m_cond.wait(lock)

保证以原子方式解锁互斥锁,为线程释放它,并开始监听条件变量的通知(参见例如here)。这种原子性还确保线程中的任何代码在设置监听后执行(因此不会错过任何通知调用)。这当然假设线程首先锁定互斥锁,否则所有赌注都关闭。

另一个需要理解的重要一点是条件变量可能会受到“虚假唤醒”的影响,因此有第二个布尔条件很重要(例如,在这里,您可以检查队列是否为空),这样您就不会最终被唤醒一个空队列。像这样的东西:

m_cond.wait(lock, [this]() { return !m_queue.empty() || m_exit; });
于 2018-07-24T10:05:11.063 回答