未来的可组合性,以及boost :: wait_for_all

时间:2013-01-05 09:04:45

标签: c++ multithreading boost c++11 future

我刚刚阅读了文章“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();

这种可组合性方法有什么问题?这是实现可组合性的有效方法吗?我们在这种模式中需要一些优雅的语法功能吗?

2 个答案:

答案 0 :(得分:5)

when_anywhen_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_allfi上都被屏蔽,在有人分别执行fi2pt之前,这两种情况都不会完成。没有人会执行它们,因为我的程序被阻止了!

你可能意味着我写这样的东西:

pt2

将有效。但它效率极低 - 我们生成三个工作线程(通过三次调用std::async(pt); std::async(pt2); std::async(pt_composite); int result = fi_composite.get(); ),以便执行两个线程&#39;值得的工作。第三个线程 - 运行async的线程 - 将立即生成,然后就坐在那里,直到pt_compositept完成运行。这比旋转更好,但它比不存在更糟糕:这意味着我们的线程池比应该拥有的工作者少一个。在一个看似合理的线程池实现中,每个CPU核心只有一个线程,并且很多任务一直在进行,这意味着我们只有一个CPU核心处于空闲状态,因为工作线程是意味着在该核心上运行,目前在pt2内被阻止。

我们想要做的是以声明方式声明我们的意图:

wait_for_all

然后让库和调度程序一起工作做正确的事情:不要产生任何不能立即继续执行任务的工作线程。如果最终用户必须编写明确睡眠,等待或阻塞的单行代码,那么某些性能肯定会丢失。

换句话说: 在它的时间之前没有工作线程。

显然,可能在标准C ++中完成所有这些操作,没有库支持;这就是图书馆本身的实施方式!但是从头开始实施是一个巨大的痛苦,有许多微妙的陷阱;这就是为什么图书馆支持似乎即将到来的好处。

N3428中提到的ISO提案Roshan Shariff's answer已更新为N3857N3865提供了更多便利功能。