Leandro
Leandro

Reputation: 132

How do unique locks work compared to normal mutex locks?

I've came across with this sample of code provided by a book. Btw this book has bad reviews. I regret that I bought it

std::mutex m_mutex;
mutable std::unique_lock<std::mutex> m_finishedQueryLock{ m_mutex, std::defer_lock };

bool m_playerQuit{ false };
void SetPlayerQuit()
{
    m_finishedQueryLock.lock();
    m_playerQuit = true;
    m_finishedQueryLock.unlock();
}

I'm not satisfied with the book's explanation of how it works and why should I use it. I already have an idea of how mutex locks work and the implementations of it, but I have a difficulty understanding the second line of the code above. And why does it have a mutable keyword in it?

I'm completely new on C++ Programming. So a basic level of explanation would help me a lot.

Upvotes: 3

Views: 2298

Answers (3)

incrediblehulk
incrediblehulk

Reputation: 409

All answers explain very well why the example is not good. I will answer when you would want to use a std::unique_lock instead of a std::lock_guard.

When a thread is waiting on something, one may use a std::condition_variable. A condition variable makes use of deferred locking and therefore uses a unique_lock, which allows deferred locking. I don't think you explicitly need to say you need deferred locking, as in the example.

To grasp these concepts, you first need to learn what RAII is. Then have a look at this reference. An example of the condition variables can be found here.

Upvotes: 0

Jonathan Wakely
Jonathan Wakely

Reputation: 171303

That example looks totally stupid.

The second line is declaring a non-static data member, and

  • the member is mutable (for reasons given below);
  • the member is an object of type std::unique_lock<std::mutex>, which is a helper type for locking/unlocking an associated mutex object;
  • the member is initialized when an instance of the class is created, by calling its constructor and passing m_mutex and the special tag std::defer_lock as arguments.

But doing that is stupid, and I'm not surprised the book has bad reviews if it has examples like that.

The point of unique_lock is to lock the associated mutex, and then to automatically unlock it when it goes out of scope. Creating a unique_lock member like this is stupid, because it doesn't go out of scope at the end of the function, so the code has absolutely no advantage over:

mutable std::mutex m_mutex;

bool m_playerQuit{ false };

void SetPlayerQuit()
{
    m_mutex.lock();
    m_playerQuit = true;
    m_mutex.unlock();
}

But this manual unlocking has all the problems that unique_lock was designed to solve, so it should be using a scoped lock (either unique_lock or lock_guard) but only in the function scope, not as a member:

mutable std::mutex m_mutex;

bool m_playerQuit{ false };

void SetPlayerQuit()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    m_playerQuit = true;
}   // m_mutex is automatically unlocked by the ~lock_guard destructor

The mutable keyword is necessary so that you can lock the mutex in const member functions. Locking and unlocking a mutex is a non-const operation that modifies the mutex, which would not be allowed in a const member if it wasn't mutable.

Upvotes: 8

stefaanv
stefaanv

Reputation: 14392

The unique_lock is a RAII system. When created, taking the mutex as parameter, it will lock the mutex and when leaving the scope it is destroyed, thus unlocking the mutex. When you need to unlock earlier, you can call the unlock() function like in your example.

Using the unique_lock like in your example doesn't create added value over using the mutex directly.

Upvotes: 1

Related Questions