某些内核对信号量提供“ flush”操作,以解除阻止所有等待信号量的任务。
例如,VxWorks具有semFlush() API,该API以原子方式取消阻止在指定信号量上的所有任务,即在允许任何任务运行之前,将取消阻止所有任务。
我正在Linux上实现一个C ++类,其行为类似于Binary Semaphore,并且具有此“ flush”功能。不幸的是,Linux上的semaphore.h不提供类似API的flush()或broadcast()。
我尝试过的方法:使用condition variables实现二进制信号量。这是我的伪代码:
class BinarySem
{
BinarySem();
bool given;
mutex m;
condition_var cv;
give();
take();
take( Timeout T );
tryTake();
flush();
}
BinarySem::BinarySem()
: given(false)
{}
// take(Timeout T), tryTake() not shown
// to make question concise on StackOverflow
BinarySem::give()
{
{
lock_guard lk(m);
given = true;
}
cv.notify_one();
}
BinarySem::flush()
{
{
lock_guard lk(m);
given = true;
}
cv.notify_all();
}
BinarySem::take()
{
unique_lock lk(m);
while(!given)
{
cv.wait(lk);
}
given = false;
lk.unlock();
}
但是,此flush()
行为不正确。假设我们有2个线程在BinarySem上等待(即它们都调用了take()
)。让这些线程为hiPrioThread
和loPrioThread
。
当在flush()
对象上调用BinarySem
时,hiPrioThread
从take()
退出并运行。当它屈服时(hiPrioThread
只是屈服,它尚未退出),因为布尔值loPrioThread
现在又变为given
,所以false
仍将无法运行。必须使用布尔值才能防止虚假唤醒。
相反,信号量的flush()
函数应仅解除阻塞所有线程,并且只要有机会它们就可以运行。
如果我没有在given = false
的末尾设置take()
怎么办?这将使我的代码容易受到虚假唤醒,然后在使用give()
时,多个线程可能会畅通无阻。
有人有什么建议吗?
借用某些“ CyclicBarrier”实现中的概念,并具有一个生成或周期计数器。
“刷新”信号灯正在推动这一代的发展。每个接受者在等待之前都会记录其生成,并且接受者等待信号量为given
或以便更改生成:
BinarySem::flush() {
{
lock_guard lk(m);
current_gen++; // "flush" all waiters from the previous gen
given = true; // make sem available for the new generation
}
cv.notify_all();
}
BinarySem::take() {
lock_guard lk(m);
uint64_t my_generation = current_gen;
while (!given && my_generation == current_gen) {
cv.wait(lk);
}
if (my_generation == current_gen) {
given = false;
}
}
((警告:未经测试)