假设我有一个包含std::queue
的ThreadQueue类,并且我将每个std::ref
的实例传递给线程。进一步假设,线程1(主线程)创建并保存ThreadQueue对象,并将消息倒入其中,第二个线程的任务是将这些消息传入时将其放入并放置在某个地方,例如,将其写入日志文件。
该类如下所示:
#include <queue>
#include <mutex>
#include <condition_variable>
using namespace std;
template <typename T>
class ThreadQueue
{
queue<T> q_;
mutex mtx;
unique_lock<mutex> lck;
condition_variable cv;
public:
ThreadQueue() { lck = unique_lock<mutex>(mtx); }
~ThreadQueue() { if (lck.owns_lock()) lck.unlock(); }
void enqueue (const T&);
T dequeue ();
};
template <typename T>
void ThreadQueue<T>::enqueue (const T& t)
{
lck.lock();
q_.push(t);
lck.unlock();
cv.notify_one();
}
template <typename T>
T ThreadQueue<T>::dequeue ()
{
cv.wait(lck);
lck.lock();
T t = q_.front(); // let's assume that's a copy assignment, because
q_.pop(); // pop() calls the descructor.
lck.unlock();
return t;
}
然后主要是:
ThreadQueue<std::pair<int, std::string>> logs;
// and maybe something like:
std::thread logger(std::ref(logs));
关键行是
cv.wait(lck);
文档明确指出lck
必须是unique_lock对象,其互斥对象当前已被此线程锁定。现在的问题是:谁真正锁定互斥锁,谁拥有锁,线程1或线程2?
最佳答案
代码中有两个主要错误:
unique_lock
不应是成员变量。它必须在堆栈上创建,以便在保留作用域(正常返回或发生异常)时自动为您释放锁定。 cv.wait
。 std::condition_variable
是一种无状态的通信机制,如果在没有服务员的情况下发出信号,则信号会丢失。也有虚假的唤醒。您可能想使用cv.wait([this] { return !q_.empty(); });
为您正确处理条件变量的等待。 例如。:
using namespace std;
template <typename T>
class ThreadQueue
{
queue<T> q_;
mutex mtx;
condition_variable cv;
public:
void enqueue (const T&);
T dequeue ();
};
template <typename T>
void ThreadQueue<T>::enqueue (const T& t)
{
{
lock_guard<mutex> lck(mtx);
q_.push(t);
}
cv.notify_one(); // Optimization: release the lock before signalling.
}
template <typename T>
T ThreadQueue<T>::dequeue ()
{
unique_lock<mutex> lck(mtx);
cv.wait(lck, [this] { return !q_.empty(); });
T t = q_.front();
q_.pop();
return t;
}
Who owns the lock?
锁定互斥锁的线程拥有该锁,或者进入了关键部分。这里的
std::lock_guard
和std::unique_lock
都将互斥锁锁定在构造函数中,并在析构函数中解锁(在正常范围退出或异常时)。
关于c++ - condition_variable,引用和线程:谁拥有锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60156002/