有没有办法在 Linux 上以原子方式刷新 C++ 中的二进制信号量?
Is there a way to atomically flush a binary semaphore in C++ on Linux?
一些内核在信号量上提供了一个“flush”操作来解锁所有等待信号量的任务。
例如,VxWorks 有一个 semFlush() API 可以自动解除阻塞所有挂起在指定信号量上的任务,即所有任务都将在允许 运行 之前解除阻塞。
我正在 Linux 上实现 C++ class,它的行为类似于二进制信号量,并且还具有此 "flush" 功能。不幸的是,Linux 上的 semaphore.h 没有像 APIs.
那样提供 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 Whosebug
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()
时多个线程可能会畅通无阻。
有人有什么建议吗?
从一些 "CyclicBarrier" 实现中借用一个概念,并有一个生成或循环计数器。
"Flushing" semaphore 则在推进生成。每个taker在等待之前都会记下自己的generation,taker等待信号量为given
or generation发生变化:
BinarySem::flush() {
{
lock_guard lk(m);
current_gen++; // "flush" all waiters from the previous gen
//given = true; // No need to give; the 'current' taker will do this when done
}
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;
}
}
(警告:未经测试)
一些内核在信号量上提供了一个“flush”操作来解锁所有等待信号量的任务。
例如,VxWorks 有一个 semFlush() API 可以自动解除阻塞所有挂起在指定信号量上的任务,即所有任务都将在允许 运行 之前解除阻塞。
我正在 Linux 上实现 C++ class,它的行为类似于二进制信号量,并且还具有此 "flush" 功能。不幸的是,Linux 上的 semaphore.h 没有像 APIs.
那样提供 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 Whosebug
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()
时多个线程可能会畅通无阻。
有人有什么建议吗?
从一些 "CyclicBarrier" 实现中借用一个概念,并有一个生成或循环计数器。
"Flushing" semaphore 则在推进生成。每个taker在等待之前都会记下自己的generation,taker等待信号量为given
or generation发生变化:
BinarySem::flush() {
{
lock_guard lk(m);
current_gen++; // "flush" all waiters from the previous gen
//given = true; // No need to give; the 'current' taker will do this when done
}
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;
}
}
(警告:未经测试)