是否存在允许在不持有互斥锁的情况下使用std::condition_variable::notify_once/all
的模式?(注意:我不是在谈论wait
部分)
正式地,在条件变量上调用notify
时,不需要线程持有互斥锁(与wait()
相反),但是 - 据我所知 - 以下是不可能的:
void Thread1() {
unique_lock<mutex> ul(mux);
while (!newData) { //*) see below
cv.wait(ul);
}
//readData;
}
void Thread2() {
//write/modify Data;
newData = true;
cv.notify_one();
}
问题是Thread1在检查newData
之后,在它实际开始等待之前,可能会在它进入循环体时被中断。
现在,如果Thread2在这两个语句之间运行完成,则Thread1将永远不会注册通知,而Thread1将永远等待。
simple
显而易见的解决方案是将通知发送到受保护区域:
void Thread2() {
//write/modify Data;
newData = true;
{
lock_guard<mutex> lg(mux);
cv.notify_one();
}
}
为什么默认情况下这是必要的进一步讨论here 然而,额外的开销困扰我,特别是考虑到99%的时间锁定是不必要的。此外,我需要一个同步机制来保护另一个同步机制这一事实对我来说似乎不对。
所以我想知道,是否存在一种模式(例如使用原子操作)使得在通知端锁定 是不必要的(仅使用标准的c ++ 14函数/机制)。
编辑:
详细说明背景:我有一个无阻塞的循环缓冲区,具有非阻塞读写。现在我想添加阻塞读取和写入(等待缓冲区已满/空),但我想最小化添加机制对非阻塞性能的影响。显然,我无法避免,例如在写入中调用nonEmpty.notify_all()
,但我真的想避免在该函数中锁定互斥锁。
到目前为止,我提出了两个想法,遗憾的是它们不太理想:
使用wait_for
。这样,即使错过了通知,线程也不会永远死锁,但它会引入零星的延迟。
锁定通知,但是根据另一个线程是否可能存在于有问题的部分(类似于this),使整个块成为条件。这应该会显着减少通知siede上的系统调用次数,但有时仍需要互斥锁,这可能会限制其可扩展性(我还没有进行任何测量)
*)顺便说一句。我知道wait
的谓词版本。不幸的是 - 根据cppreference - 它在语义上等同于我上面的版本,并且显式形式使得更容易推理该程序。