lundi 10 février 2020

condition_variable, references and threads: Who Owns The Lock?

Supposing I have a class ThreadQueue holding a std::queue, and I pass an instance of it per std::ref to a thread. Supposing further, that thread 1 (the main thread) creates and holds the ThreadQueue object and will pour messages into it, and the second thread's task is to take these messages as they come and put them somewhere, say, write them to a logfile.

The class looks like:

#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;
}

Then in main the tune goes:

ThreadQueue<std::pair<int, std::string>> logs;
// and maybe something like:
std::thread logger(std::ref(logs));

The crucial line is cv.wait(lck); The documentation clearly states that lck is required to be a unique_lock object whose mutex object is currently locked by this thread.

The question now is: who does actually lock the mutex, and who owns the lock, thread 1 or thread 2?

Aucun commentaire:

Enregistrer un commentaire