我刚刚阅读了文章“Futures Done Right”,而c ++ 11所承诺的主要缺点似乎是从现有的未来创造复合期货
我现在正在查看boost::wait_for_any
的文档但请考虑以下示例:
int calculate_the_answer_to_life_the_universe_and_everything()
{
return 42;
}
int calculate_the_answer_to_death_and_anything_in_between()
{
return 121;
}
boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything);
boost:: future<int> fi=pt.get_future();
boost::packaged_task<int> pt2(calculate_the_answer_to_death_and_anything_in_between);
boost:: future<int> fi2=pt2.get_future();
....
int calculate_the_oscillation_of_barzoom(boost::future<int>& a, boost::future<int>& b)
{
boost::wait_for_all(a,b);
return a.get() + b.get();
}
boost::packaged_task<int> pt_composite(boost::bind(calculate_the_oscillation_of_barzoom, fi , fi2));
boost:: future<int> fi_composite=pt_composite.get_future();
这种可组合性方法有什么问题?这是实现可组合性的有效方法吗?我们在这种模式中需要一些优雅的语法功能吗?
答案 0 :(得分:5)
when_any
和when_all
是构成期货的完美有效方式。它们都对应于并行组合,其中复合操作等待一个或所有组合操作。
我们还需要顺序组合(不在Boost.Thread中)。例如,这可以是future<T>::then
函数,它允许您对使用未来值的操作进行排队,并在未来准备就绪时运行。可以自己实现这一点,但需要进行效率权衡。 Herb Sutter在他的recent Channel9 video中谈到了这一点。
N3428是一个将这些功能(以及更多)添加到C ++标准库的提案草案。它们都是库功能,不会为该语言添加任何新语法。此外,N3328是为可恢复函数添加语法的提议(例如在C#中使用async
/ await
),它将在内部使用future<T>::then
。
答案 1 :(得分:3)
使用edulcorant一词的要点。 :)
示例代码的问题在于您将所有内容打包成任务,但您从未计划执行这些任务!
int calculate_the_answer_to_life() { ... }
int calculate_the_answer_to_death() { ... }
std::packaged_task<int()> pt(calculate_the_answer_to_life);
std::future<int> fi = pt.get_future();
std::packaged_task<int()> pt2(calculate_the_answer_to_death);
std::future<int> fi2 = pt2.get_future();
int calculate_barzoom(std::future<int>& a, std::future<int>& b)
{
boost::wait_for_all(a, b);
return a.get() + b.get();
}
std::packaged_task<int()> pt_composite([]{ return calculate_barzoom(fi, fi2); });
std::future<int> fi_composite = pt_composite.get_future();
如果此时我写了
pt_composite();
int result = fi_composite.get();
我的程序将永久阻止。 它永远不会完成,因为pt_composite
被阻止calculate_barzoom
被阻止{{1} }},在wait_for_all
和fi
上都被屏蔽,在有人分别执行fi2
或pt
之前,这两种情况都不会完成。没有人会执行它们,因为我的程序被阻止了!
你可能意味着我写这样的东西:
pt2
此将有效。但它效率极低 - 我们生成三个工作线程(通过三次调用std::async(pt);
std::async(pt2);
std::async(pt_composite);
int result = fi_composite.get();
),以便执行两个线程&#39;值得的工作。第三个线程 - 运行async
的线程 - 将立即生成,然后就坐在那里,直到pt_composite
和pt
完成运行。这比旋转更好,但它比不存在更糟糕:这意味着我们的线程池比应该拥有的工作者少一个。在一个看似合理的线程池实现中,每个CPU核心只有一个线程,并且很多任务一直在进行,这意味着我们只有一个CPU核心处于空闲状态,因为工作线程是意味着在该核心上运行,目前在pt2
内被阻止。
我们想要做的是以声明方式声明我们的意图:
wait_for_all
然后让库和调度程序一起工作做正确的事情:不要产生任何不能立即继续执行任务的工作线程。如果最终用户必须编写明确睡眠,等待或阻塞的单行代码,那么某些性能肯定会丢失。
换句话说: 在它的时间之前没有工作线程。
显然,可能在标准C ++中完成所有这些操作,没有库支持;这就是图书馆本身的实施方式!但是从头开始实施是一个巨大的痛苦,有许多微妙的陷阱;这就是为什么图书馆支持似乎即将到来的好处。
N3428中提到的ISO提案Roshan Shariff's answer已更新为N3857,N3865提供了更多便利功能。