【发布时间】:2020-06-15 06:14:59
【问题描述】:
一些内核在信号量上提供“flush”操作来解除阻塞等待信号量的所有任务。
例如,VxWorks 有一个semFlush() API,它可以原子地解除所有挂在指定信号量上的任务,即,在允许运行之前,所有任务都会被解除阻塞。
我正在 Linux 上实现一个 C++ 类,它的行为类似于二进制信号量,并且还具有这种“刷新”功能。不幸的是,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。
当在BinarySem 对象上调用flush() 时,hiPrioThread 将从take() 退出并运行。
当它产生时(hiPrioThread 只是产生,它还没有退出),loPrioThread 仍然无法运行,因为布尔值 given 现在又是 false。需要布尔值来防止虚假唤醒。
相反,信号量的flush() 函数应该只是解除对所有线程的阻塞,只要有机会它们就可以运行。
如果我没有在take() 的末尾设置given = false 怎么办?这将使我的代码容易受到虚假唤醒的影响,然后当使用 give() 时,多个线程可能会被解除阻塞。
有人有什么建议吗?
【问题讨论】:
-
semaphore.h是过时的 sysv 接口。条件变量和 notify_all 与此处描述的“信号量”相匹配。 notify_all 应该足够了。另请注意,notify_all 应在仍持有关联互斥锁的情况下调用,除非您谨慎地管理它们的顺序非常。如果 notify_all 由于某种原因不起作用,您需要显示一个 minimal reproducible example 来演示该问题,而不是伪代码。
标签: c++ linux multithreading semaphore condition-variable