在Linux上的C ++中,有没有一种方法可以自动刷新二进制信号量?

问题描述 投票:3回答:1

某些内核对信号量提供“ 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())。让这些线程为hiPrioThreadloPrioThread

当在flush()对象上调用BinarySem时,hiPrioThreadtake()退出并运行。当它屈服时(hiPrioThread只是屈服,它尚未退出),因为布尔值loPrioThread现在又变为given,所以false仍将无法运行。必须使用布尔值才能防止虚假唤醒。

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

如果我没有在given = false的末尾设置take()怎么办?这将使我的代码容易受到虚假唤醒,然后在使用give()时,多个线程可能会畅通无阻。

有人有什么建议吗?

c++ linux multithreading semaphore condition-variable
1个回答
0
投票

借用某些“ 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;
  }
}

((警告:未经测试)

© www.soinside.com 2019 - 2024. All rights reserved.