等待多个未来?

Waiting for multiple futures?

提问人:alveko 提问时间:10/7/2013 最后编辑:Konrad Rudolphalveko 更新时间:11/15/2021 访问量:30577

问:

我想运行相同类型的任务(工作线程),但一次不超过一定数量的任务。当任务完成时,其结果是新任务的输入,然后可以启动该任务。

有什么好的方法可以在 C++11 中使用异步/未来范式实现这一点吗?

乍一看,它看起来很简单,您只需生成多个任务:

std::future<T> result = std::async(...);

然后,运行以获取任务的异步结果。result.get()

然而,这里的问题是,未来的对象必须存储在某种队列中,并一个接一个地等待。但是,可以一遍又一遍地遍历未来的对象,检查它们是否已准备就绪,但由于不必要的 CPU 负载,因此不需要这样做。

是否有可能以某种方式等待给定集合的任何未来准备好并获得其结果?

到目前为止,我能想到的唯一选择是没有任何异步/未来的老式方法。具体来说,生成多个工作线程,并在每个线程的末尾将其结果推送到受互斥锁保护的队列中,通过条件变量通知等待线程队列已更新了更多结果。

有没有其他更好的异步/未来解决方案?

C++ 多线程 C++11

评论

12赞 Xeo 10/7/2013
在 C++14 中,这将是 ,但就目前而言,你有点不走运。when_any(futures...).then(foo)
5赞 ComicSansMS 10/7/2013
使用 Boost,您可以获得wait_for_any。有了香草C++11,你现在就不走运了。
1赞 alveko 10/7/2013
@Xeo,@ComicSansMS,谢谢你的提示!有关 C++14 的更多信息,请点击此处。Boost 的wait_for_any正是我想要的。AFAIU,它与不支持注册外部服务员 (cvs) 的 std::future 不兼容,对吧?when_any
1赞 Lightness Races in Orbit 10/8/2013
spawning multiple worker threads and at the end of each thread push its result into a mutex-protected queue对我来说听起来不错。如果工作线程只是偶尔检查队列大小≥ 1,则甚至不需要通知。
3赞 je4d 10/8/2013
@Xeo很抱歉令人失望,但我不认为/进入了 C++14。我认为他们正朝着在芝加哥启动的并发 TS 前进。when_any.then(...)

答:

6赞 Sebastian Mach 10/7/2013 #1

你可以创造“第一代”的所有未来,并将所有这些未来交给你的第二代任务,然后他们自己等待他们的输入。

评论

0赞 alveko 10/7/2013
+1 的好主意!不幸的是,它在我的程序中不起作用,其中调度任务背后的逻辑比我在问题中描述的要复杂一些。
21赞 Yakk - Adam Nevraumont 10/7/2013 #2

C++11 中的线程支持只是第一次通过,虽然很不稳定,但它还不支持多次等待。std::future

但是,您可以相对低效地伪造它。你最终会为每个线程创建一个帮助线程(哎呀,非常昂贵),然后将他们的“这已经准备好了”收集到一个同步的多生产者单消费者消息队列中,然后设置一个消费者任务来调度给定的准备就绪的事实。std::futurefuturestd::future

在这个系统中,没有添加太多功能,并且让任务直接声明它们已准备就绪并将其结果粘贴到上述队列中会更有效率。如果采用此路线,则可以编写与 或 模式匹配的包装器,并返回表示队列消息的类似对象。这基本上涉及重新实现并发库的块。std::futurestd::asyncstd::threadstd::future

如果你想继续使用 ,你可以创建 s,并让每个依赖任务都依赖于 s 的集合:即,在没有中央调度程序的情况下执行。这不允许像中止/关闭消息这样的事情,我认为这对于一个强大的多线程任务系统来说是必不可少的。std::futureshared_futureshared_future

最后,您可以等待 C++2x,或者每当并发 TS 折叠到标准中时,为您解决问题。

评论

4赞 Yakk - Adam Nevraumont 5/19/2015
@Steephen 对不起:这不是在 C++14 中,我太乐观了。链接到我认为可以解决添加的问题的论文。它不仅有 和 ,您还可以将 s 附加到一堆 s 上,并在其中发出一个条件变量的信号,您可以等待:工作方式很像 OP 中的“老派”方法,但内容的生产者不需要了解队列机制,他们只需要提供一个未来。仿真只能通过(最多)每个 的额外线程开销来完成,这远非理想。when_anywhen_all.thenfuture.then.thenfuture
1赞 graham.reeds 8/11/2016
C++1z 无法解决问题。虽然已经达成一致,但显然为时已晚,无法进入2017年:-(
1赞 Yakk - Adam Nevraumont 8/11/2016
@graham.Reeds点了点头。我的意思是,它在 2013 年正在研究/讨论,你可以理解我认为它可能会在 C++17 中实现。;)如果您手头有,它在哪个标准跟踪 TS 中?我可以用这些信息来改进答案。
2赞 graham.reeds 8/11/2016
期货的扩展在 2015 年初被转移到并发 TS 中,当时它们几乎已经完成。
4赞 Lothar 10/16/2020
似乎也不在 C++20 中。
1赞 ericP 3/16/2020 #3

鉴于“为多个未来而战”的标题吸引了人们提出诸如“是否需要等待未来清单?您可以通过跟踪待处理的线程来充分做到这一点:

unsigned pending = 0;
for (size_t i = 0; i < N; ++i) {
    ++pending;
    auto callPause =
        [&pending, i, &each, &done]()->unsigned {
            unsigned ret = each();
            results[i] = ret;
            if (!--pending)
                // called in whatever thread happens to finish last
                done(results);
            return ret;
        };
    futures[i] = std::async(std::launch::async, each);
}

完整示例

可以将 std::experimental::when_all 与 spread 运算符一起使用

评论

1赞 Florian Winter 10/15/2021
这个答案需要更好的解释,因为要看到它解决了什么问题,如何解决以及如何使用它是非常乏味的。您的代码片段也不完整,仅作为您链接的完整示例的一部分才有意义。
2赞 buffy 11/15/2021 #4

facebook 的愚蠢在期货上有 collectAny/collectN/collectAll,我还没有尝试过,但看起来很有希望。