简体   繁体   中英

Condition variable spurious wakeup/round robin tasks

I'm new to C++ threading and I'm trying to write a round robin (cooperative) task manager using C++ threads. Is the declaration of round_robin_task_ulock below valid - it's a global static variable. I want to defer locking the mutex until the task manager thread starts running.

static std::mutex                      round_robin_task_mutex;
static std::condition_variable         task_manager_cvar; 
static thread                          round_robin_task_manager_thread;
static std::unique_lock<std::mutex>    
     round_robin_task_ulock(round_robin_task_mutex, std::defer_lock);

static void round_robin_task_manager()
{
    round_robin_task_ulock.lock();
    //..
}

Once the task manager thread is running it can start a round robin task running with a notify followed by wait

 round_robin_tasks[current_task_id].task_cvar.notify_one();
 // release the mutex and go to sleep
 task_manager_cvar.wait(round_robin_task_ulock); 
 // now we own the mutex again

and when a round robin task wants to suspend itself it notifies the task manager thread

 task_manager_cvar.notify_one();
 round_robin_tasks[current_task_id].task_cvar.wait
                  (round_robin_task_ulock, [] { return  
                      round_robin_tasks[my_task_id].task_running_now; } );

Are there any obvious problems with this? I've read about spurious wake. Is it possible that a spurious wake could wake up the task manager thread while a round robin task still owned the mutex or is it guaranteed that when the wait function returns, the mutex is always owned (locked) by the thread that called wait.

You should build your code so that without any condition variable it would still work. If it would work if it locked a mutex, checked for the condition, unlocked the mutex and immediately repeated the loop, then it will work with a condition variable, only more efficiently.

An implementation could wake your condition variables every 10 milliseconds, if it wanted to for some reason, or if there was some weird interaction with instructions like MWAIT, cache lines and Transactional Locks or some other strange hardware feature.

I seem to recall reading about an architecture that would signal wait events on memory locations whenever any part of that cache line was written. And the thread library didn't pad the structure enough for the cache line size.

So YES you might have to handle hundreds of wakes per second and your code should still work correctly.

From what I see of what you've written, you have only one place that you are checking the predicate , which is the condition that must be true to proceed past the condition. And before every notify you should have updated the predicate. The predicate must always be written or read while protected by the same lock used by the condition variable.

If you want different behavior you might rather use a semaphore .

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