如何在没有轮询的情况下实现std :: when_any?

xml*_*lmx 2 c++ concurrency standards multithreading c++17

请考虑http://en.cppreference.com/w/cpp/experimental/when_any.下面只是一个幼稚简单的实现:

#include <future>

template<typename Iterator>
auto when_any(Iterator first, Iterator last)
{
    while (true)
    {
        for (auto pos = first; pos != last; ++pos)
        {
            if (pos->is_ready())
            {
                return std::move(*pos);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不满意,因为它是一个无限循环中的繁忙轮询.

有没有办法避免繁忙的民意调查?

Yak*_*ont 5

无轮询版本将在每个未来启动1个线程,并让它们设置一个条件变量,以便将来做好准备.

然后你"泄漏"线程,直到期货准备就绪,同时返回一个准备就绪的事实.

这很糟糕.但没有民意调查.

要做得更好,您需要拥有一个可以设置(并理想地删除)的延续的未来.然后你只要求期货在完成后通知你,然后等待.这需要修改或编写自己的未来.

这是延续和when_any提出标准化的原因之一.你将来需要它们.

现在,如果您拥有自己的系统,则可以将其基于线程安全队列,而不是期货,通过条件变量实现.这需要在"未来"创造时进行合作.

struct many_waiter_t {
  std::mutex m;
  std::condition_variable cv;
  std::vector<std::size_t> index;

  std::size_t wait() {
    auto l = lock();
    cv.wait(l, [this]{
      return !index.empty();
    });
    auto r = index.back();
    index.pop_back();
    return r;
  }
  void set( std::size_t N ) {
    {
      auto l = lock();
      index.push_back(N);
    }
    cv.notify_one();
  }
};
template<class T>
std::future<T> add_waiter( std::future<T> f, std::size_t i, std::shared_ptr<many_waiter_t> waiter )
{
  return std::async([f = std::move(f), waiter, i]{
    auto r = f.get();
    waiter.set(i);
    return r;
  });
}
Run Code Online (Sandbox Code Playgroud)

通过一系列期货fs,我们可以生成一个新的期货f2s和服务员数组,这样服务员可以非自旋锁等待直到未来准备好,并且f2s对应于原始fs.

您可以反复等待,waiter直到f2s全部准备​​就绪.