不同线程间的互斥锁同步
mutex lock synchronization between different threads
由于我最近开始编写多线程程序,所以这可能是一个愚蠢的问题。我发现了很棒的互斥锁和条件变量用法。据我所知,用途是:
- 保护 code/shared 部分资源不被多线程访问破坏。因此锁定该部分,从而可以控制将访问哪个线程。
- 如果一个线程正在等待来自另一个线程的 resource/condition,可以使用 cond.wait() 而不是每毫秒轮询一次
现在考虑以下 class 示例:
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才能拿到锁?
我在析构函数实现中也看到过这种模式,使用相同的 class 成员互斥锁,析构函数通知所有线程(class 方法)是时候终止它们各自的 loops/functions等等
正如 Jarod 在评论中提到的,电话
m_cond.wait(lock)
保证以原子方式解锁互斥锁,为线程释放它,并开始监听条件变量的通知(参见 here)。
这种原子性还确保在设置监听后线程中的任何代码都被执行(因此不会错过任何通知调用)。这当然假设线程首先锁定互斥量,否则所有赌注都关闭。
另一个需要理解的重要一点是条件变量可能会受到“spurious wakeups”的影响,因此拥有第二个布尔条件很重要(例如,在这里,您可以检查队列是否为空)以便您不会以空队列醒来。像这样:
m_cond.wait(lock, [this]() { return !m_queue.empty() || m_exit; });
由于我最近开始编写多线程程序,所以这可能是一个愚蠢的问题。我发现了很棒的互斥锁和条件变量用法。据我所知,用途是:
- 保护 code/shared 部分资源不被多线程访问破坏。因此锁定该部分,从而可以控制将访问哪个线程。
- 如果一个线程正在等待来自另一个线程的 resource/condition,可以使用 cond.wait() 而不是每毫秒轮询一次
现在考虑以下 class 示例:
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才能拿到锁?
我在析构函数实现中也看到过这种模式,使用相同的 class 成员互斥锁,析构函数通知所有线程(class 方法)是时候终止它们各自的 loops/functions等等
正如 Jarod 在评论中提到的,电话
m_cond.wait(lock)
保证以原子方式解锁互斥锁,为线程释放它,并开始监听条件变量的通知(参见 here)。 这种原子性还确保在设置监听后线程中的任何代码都被执行(因此不会错过任何通知调用)。这当然假设线程首先锁定互斥量,否则所有赌注都关闭。
另一个需要理解的重要一点是条件变量可能会受到“spurious wakeups”的影响,因此拥有第二个布尔条件很重要(例如,在这里,您可以检查队列是否为空)以便您不会以空队列醒来。像这样:
m_cond.wait(lock, [this]() { return !m_queue.empty() || m_exit; });