考虑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);
}
}
}
}
我不满意,因为它是一个无限循环中的繁忙轮询。
有没有办法避免繁忙的轮询?
答案 0 :(得分:5)
无轮询版本将在未来启动1个线程并让它们设置一个条件变量,以便将来做好准备。
然后你&#34;泄漏&#34;直到期货准备就绪的线程,同时返回一个准备就绪的事实。
这很糟糕。但没有民意调查。
为了做得更好,你需要拥有一个可以设置(并理想地删除)的延续的未来。然后你只要求期货在完成后通知你,然后等待。这需要修改或编写自己的未来。
这是继续和when_any
都被提议用于标准化的原因之一。你将来需要它们。
现在,如果您拥有自己的系统,则可以将其基于线程安全队列,而不是期货,通过条件变量实现。这需要在&#34;未来&#34;创建
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;
});
}
使用期货fs
的数组,我们可以生成一个新的期货f2s
数组和一个服务员,这样服务员可以等待非自旋锁,直到未来准备就绪,并且f2s
对应原始fs
。
您可以反复等待waiter
,直到f2s
都准备就绪。
答案 1 :(得分:3)
实际上,没有延续的期货的实用性非常有限。
如果您被迫执行此操作并使用std::future
,我建议您使用.wait_for()
通过increasing timeouts进行更智能的投票。
答案 2 :(得分:1)
I have posted an implementation of when_any
over on CodeReview.正如Yakk在回答中所说,
为了做得更好,你需要拥有一个可以设置(并理想地删除)的延续的未来。然后你只要求期货在完成后通知你,然后
wait
。这需要修改或编写自己的未来。
所以我的实现依赖于future::then()
,其主旨是:
template<class... Futures>
struct when_any_shared_state {
promise<tuple<Futures...>> m_promise;
tuple<Futures...> m_tuple;
std::atomic<bool> m_done;
std::atomic<bool> m_count_to_two;
when_any_shared_state(promise<tuple<Futures...>> p) :
m_promise(std::move(p)), m_done(false), m_count_to_two(false) {}
};
template<class... Futures>
auto when_any(Futures... futures) -> future<tuple<Futures...>>
{
using shared_state = detail::when_any_shared_state<Futures...>;
using R = tuple<Futures...>;
promise<R> p;
future<R> result = p.get_future();
auto sptr = make_shared<shared_state>(std::move(p));
auto satisfy_combined_promise =
[sptr](auto f) {
if (sptr->m_done.exchange(true) == false) {
if (sptr->m_count_to_two.exchange(true)) {
sptr->m_promise.set_value(std::move(sptr->m_tuple));
}
}
return f.get();
};
sptr->m_tuple = tuple<Futures...>(futures.then(satisfy_combined_promise)...);
if (sptr->m_count_to_two.exchange(true)) {
sptr->m_promise.set_value(std::move(sptr->m_tuple));
}
return result;
}
您为每个传入的future
附加了一个续集(使用then
)。此延续将shared_ptr
保持为共享状态。共享状态包含count-to-one(m_done
)和count-to-two(m_count_to_two
)。执行的每个继续将增加count-to-one;如果它是赢家,它也会增加计数到两个。在完成所有这些操作后,主线程也会增加count-to-two。一旦计数到2已达到2(表示主线程已完成设置并且至少有一个延续已执行),我们将在对应的承诺上调用set_value
到when_any
回归未来。当当!