Search code examples
c++multithreadingmutexatomiccondition-variable

condition_variable's speed to acquire lock is low


This problem is about condition_variable.wait() function. I figure that it is probably not locking the unique_lock immediately when it is notified. Let me show my code and you would better understand my test.

Note: compiler g++, std=c++14

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <atomic>
#include <future>

using namespace std;

mutex global_mut;
condition_variable global_cond;
atomic<bool> bval;
atomic<int> ival;

void accLock() {
  unique_lock<mutex> lock(global_mut);
  while (!bval.load()) {
    global_cond.wait(lock);
  }
  cout << __PRETTY_FUNCTION__ << " get the lock" << endl;
  ival.store(2, memory_order_release);
  lock.unlock();
}

void getVal() {
  lock_guard<mutex> lock(global_mut);
  cout << __PRETTY_FUNCTION__ << " get the lock with " << ival.load(memory_order_acquire) << endl;
}

int main(int argc, char** argv) {
  bval.store(false);
  ival.store(0, memory_order_release);

  // now my global_cond should be waiting for being notified
  std::future<void> fut = std::async(std::launch::async, accLock);

  // now my global_cond should be awaken and lock global_mut  
  bval.store(true);
  global_cond.notify_one();

  // getVal should be waiting for global_mut to be unlocked
  getVal();
  return 0;
}

Idealy, I want my accLock thread to lock the mutex first and change ival, in such way that getVal() could load the latest ival, which is 2. I expect to see output like

void accLock() get the lock
void getVal() get the lock with 2

But in reality, this out is

void getVal() get the lock with 0
void accLock() get the lock

Obviously, this unique_lock didn't lock "immediately" in global_cond, letting the lock_guard in getVal() get the mutex first. May I ask what is the correct way to implement what I want? Do I understand correctly about condition_variable? Thanks.

NOTE: I use memory_order_acl and release because I thought this could help me 'correct' the order. But it not works tho.


Solution

  • When two threads contend for a mutex, it's arbitrary which one will get it. If you want one thing to happen before something else happens, it's your obligation to write code to make that happen. A mutex won't enforce any particular ordering.

    If you don't want the getVal to run until the other thread finishes, you have write some code to wait for it to finish. You can use the mutex and condition variable to do this, but you didn't.

    Generally speaking, the implementation tries to be as efficient as possible subject to the constraints you impose on it. Stopping the thread that calls getVal is inefficient (since all its code is hot in the cache and it's already been scheduled), so the implementation doesn't do it.

    The implementation has no way to know what you want and it wouldn't make sense for it to do things inefficiently in the hopes that maybe that's what you really wanted but didn't tell it.

    Note that you might get a different result in a later run. The order in which ready-to-run threads execute is unpredictable unless you make it predictable. Both threads are ready-to-run, so you can't expect any particular reliable ordering. It will be whatever the implementation thinks is best under the circumstances.