据我所知,条件变量通常需要检查虚假唤醒。例如,工作线程可能会在条件变量通知中醒来,而实际上并没有准备好要执行的工作。
所以我们做这样的事情:
condition_variable .wait (lock, [&] () {return jobs.size() > 0;});
execute_job (jobs.pop());
好的,但是现在我在execute_job
函数中。我有第二个条件变量,它提供了对其他读取器线程的合理调度的访问权限
void execute_job (job & j)
{
current_job = & j;
while (! j.finished ())
{
std::unique_lock <std::mutex> lock (reader_mutex);
j.do_some_of_it();
reader_condition.notify_one ();
reader_condition.wait (lock);
}
current_job = nullptr;
}
阅读器线程有时会(希望不要等待很长时间)
void inspect_current_job ()
{
std::unique_lock <std::mutex> lock (reader_mutex);
if (current_job)
inspect (current_job);
reader_condition.notify_one ();
}
此代码安全且正确吗,特别是考虑到我没有检查reader_mutex
上的虚假唤醒吗?
答案 0 :(得分:0)
不要设计代码以使互斥锁在所有或大部分时间都处于保持状态。这就是导致您的公平问题的原因。
听起来像您想要一个赋予一个线程优先级的锁,这样,如果优先级线程正在等待该锁,则非优先级线程将停止。您的工作线程应使用非优先级锁定功能,以便任何等待的读者都将其停止。
class prio_lock
{
private:
std::mutex m;
std::condition_variable cv;
int locked = 0;
int waiting_priority = 0;
public:
void lock(bool priority)
{
std::unique_lock<std::mutex> lk(m);
// wait for all prior priority locks
while (waiting_priority > 0)
cv.wait(lk);
if (priority)
++waiting_priority;
// wait for lock to be unlocked
while (locked != 0)
cv.wait(lk);
if (priority)
--waiting_priority;
// take the lock
locked = 1;
}
void unlock()
{
std::unique_lock<std::mutex> lk(m);
locked = 0;
cv.notify_all();
}
};
请注意,即使非优先级线程持有该锁,优先级线程也必须等到当前的锁持有者释放该锁后,才能进行前进。这是您的设计的结果,我强烈怀疑这是不好的。线程不应该持有其他线程在执行大量实际工作时可能需要的锁,而您设计中的任何内容都应重新考虑。
我不知道您的完整外部设计,但是如果读取器线程仅需要访问作业状态,则似乎写入器线程应该能够在不访问作业状态的情况下完成其大部分实际工作,并且应该保留仅在更改状态时才锁,而在进行实际工作时则不然。