有没有办法在 Linux 上用 C++ 原子地刷新二进制信号量?

Ayu*_*nha 7 c++ linux multithreading semaphore condition-variable

一些内核提供对信号量的“刷新”操作以解除对等待信号量的所有任务的阻塞。

例如,VxWorks 有一个semFlush() API,它可以原子地解除对指定信号量上挂起的所有任务的阻塞,即所有任务在允许运行之前都将被解除阻塞。

我正在 Linux 上实现一个 C++ 类,它的行为类似于二进制信号量,并且还具有这种“刷新”功能。不幸的是,Linux 上的semaphore.h不提供类似 API 的 flush() 或 broadcast() 。

我尝试过的:使用条件变量来实现二进制信号量。这是我的伪代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

但是,这flush()不会以正确的方式运行。比如说,我们有 2 个线程在等待 BinarySem(即它们都调用了take())。让这些线程成为hiPrioThreadloPrioThread

flush()BinarySem对象上调用时,hiPrioThread将退出take()并运行。当它产生时(hiPrioThread只是产生,它还没有退出),loPrioThread仍然无法运行,因为given现在false又是布尔值。需要布尔值来防止虚假唤醒。

相反,信号量的flush()函数应该只是解除所有线程的阻塞,只要有机会它们就可以运行。

如果我不在given = false末尾设置怎么办take()?这将使我的代码容易受到虚假唤醒的影响,然后多个线程在give()使用时可能会被解除阻塞。

有没有人有什么建议?

pil*_*row 4

借用一些“CyclicBarrier”实现的概念,并有一个生成或循环计数器。

然后“刷新”信号量就推进了一代。每个接受者在等待之前都会记下其生成,并且接受者等待信号量given 等待生成更改:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

(警告:未经测试)