【问题标题】:Can std::condition_variables be used as counting semaphores?std::condition_variable 可以用作计数信号量吗?
【发布时间】:2017-03-13 03:38:50
【问题描述】:

这是Can C++11 condition_variables be used to synchronize processes?的后续行动。

std::condition_variable 对象可以用作计数信号量吗?

我认为不是因为对象似乎绑定到 std::mutex,这意味着它只能用作二进制信号量。我在网上查过,包括hereherehere,但找不到使用这些对象作为计数信号量的参考或示例。

【问题讨论】:

  • 为什么要寻找类似信号量的对象?阅读上面的文字让我认为您可能需要为单核机器构建程序并希望使用该信号量机制,或者您有非常旧的代码使用围绕 OS 信号量的包装器并且您希望保留它行为。是不是有点接近了?
  • @AhiyaHiya:这是个人学习的学术练习。我正在编写有趣的代码,并尝试从基于 posix 的同步机制(pthread_mutex_t、sem_t)迁移到 C++11 原生机制。我看到 C++11 提供了std::mutex 然后很困惑为什么没有信号量。一些进一步的阅读使我了解到std::condition_variable 用于实现半信号量功能,但我正在尝试学习/理解它的功能和局限性。它似乎不完全等同于信号量,但我还不确定......还在学习。
  • 如果您正在学习并希望看到一些基础良好的多核并发/并行编程指南,您应该查看 Herb Shutter 的“有效并发”系列中的并发编程支柱:herbsutter.com/category/effective-concurrency

标签: c++ multithreading c++11 semaphore condition-variable


【解决方案1】:

是的。

struct counting_sem {
  counting_sem(std::ptrdiff_t init=0):count(init) {}
  // remove in C++17:
  counting_sem(counting_sem&& src) {
    auto l = src.lock(); // maybe drop, as src is supposed to be dead
    count = src.count;
  }
  counting_sem& operator=(counting_sem&& src) = delete;
  void take( std::size_t N=1 ) {
    if (N==0) return;
    auto l = lock();
    cv.wait(l, [&]{
      if (count > 0 && count < (std::ptrdiff_t)N) {
        N -= count;
        count = 0;
      } else if (count >= (std::ptrdiff_t)N) {
        count -= N;
        N = 0;
      }
      return N == 0;
    });
  }
  void give( std::size_t N=1 ) {
    if (N==0) return;
    {
      auto l = lock();
      count += N;
    }
    cv.notify_all();
  }
  // reduce the count without waiting for it
  void reduce(std::size_t N=1) {
    if (N==0) return;
    auto l = lock();
    count -= N;
  }
private:
  std::mutex m;
  std::condition_variable cv;
  std::ptrdiff_t count;

  auto lock() {
    return std::unique_lock<std::mutex>(m);
  }
  auto unlocked() {
    return std::unique_lock<std::mutex>(m, std::defer_lock_t{});
  }
};

代码未经测试或编译,但设计合理。

take(7) 不等同于 for(repeat 7 times) take():相反,如果这还不够,它会尽可能多地阻塞。

修改它直到有足够的东西才需要任何东西很容易:

      if (count >= (std::ptrdiff_t)N) {
        count -= N;
        N = 0;
      }

【讨论】:

  • 我明白了您的代码示例的要点,谢谢。所以我得到的印象是std::condition_variable 并不真正等价于 信号量,但它可用于创建功能上与信号量等效的对象。我想知道为什么 C++ 标准机构会采用这种方式,而不是直接提供普通的旧信号量……毕竟,它们是基础计算机科学对象。
  • @stone 因为条件变量有效地映射到硬件(我假设)并且允许的远不止信号量(上面的消息是整数计数
  • @Yakk 它不会“原子地”占用所有 7 个。 呃……但它确实……也许你的意思是 if 而不是 while。使用while,谓词将被调用一次,如果count 高于N,它将循环count 次并返回true
  • @screwnut 嗯,有点。假设你想要 7,有人给了 4。它会拿 4,然后放弃,仍然想要 3。在“原子”的情况下,它会看到只有 4,拒绝拿任何。
  • @screwnut 循环在那里是因为做数学......很难完全正确。我试图修复它,所以没有循环,但我几乎不相信它是正确的。
猜你喜欢
  • 1970-01-01
  • 2020-10-18
  • 2011-07-29
  • 1970-01-01
  • 2015-11-16
  • 1970-01-01
  • 1970-01-01
  • 2014-01-25
  • 1970-01-01
相关资源
最近更新 更多