简体   繁体   中英

Do I have to lock to check a work-available flag used with a condition variable before setting it, and if so why?

I have a worker thread in a thread pool that behaves like so:

while (running)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    m_condition.wait(lock, [&] { return m_workAvailable; });
    m_workAvailable = false;
    lock.unlock();

    // (Perform work...)
}

When I drop work into this worker thread, it looks like so:

// (enqueue the actual work item...)

if (!m_workAvailable)
{
  std::lock_guard<std::mutex> lock(m_mutex);
  m_workAvailable = true;
}

m_condition.notify_one();

After seeing the behavior of this code in practice I'm guessing that there is some kind of concurrency bug related to the

if(! m_workAvailable)

check here, which is meant to bypass contention for the mutex unless necessary. But if this is so, I don't actually see the scenario that would cause this problem.

[ Update : I have tried this with m_workAvailable being both bool and volatile bool . I have not as yet tried std::atomic<bool> , though as far as I understand that wouldn't be fundamentally different from volatile bool here. ]

Questions:

  1. Is this indeed a concurrency bug?
  2. If so, why -- what does this allow to go wrong?
  3. If so, is there any way of doing what I'm doing here that doesn't require me to lock the mutex every single time a piece of work comes in?

Is this indeed a concurrency bug?

Yes.

If so, why -- what does this allow to go wrong?

It's undefined behavior. The standards don't say what happens if a non-atomic type is accessed by one thread while another thread is, or might be, modifying it. Your code does that. So it can break in any imaginable way.

If so, is there any way of doing what I'm doing here that doesn't require me to lock the mutex every single time a piece of work comes in?

Yes, Do this:

{
     std::lock_guard<std::mutex> lock(m_mutex);
     m_workAvailable = true;
     m_condition.notify_one();
}

Your platform, if it has a good implementation, will detect that the mutex is already held when you call notify_one and avoid synchronizing again. The net effect will be roughly the same as not acquiring the mutex during the set of m_workAvailable if the mutex isn't contended. (If it is contended, the cost is unavoidable anyway.)

I have not as yet tried std::atomic<bool> , though as far as I understand that wouldn't be fundamentally different from volatile bool here.

Umm, huh? What threading standard defines the behavior when a volatile bool is accessed in one thread while another thread is, or might be, modifying it? I don't know of any except Microsoft Visual Studio. By contrast, std::atomic<bool> has well-defined semantics in that case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM