C++ std::async-依赖于实现的用法?

C++ std::async-依赖于实现的用法?,c++,c++11,stdasync,C++,C++11,Stdasync,我一直在考虑std::async,以及在未来的编译器实现中应该如何使用它。然而,现在我有点被一些感觉像是设计缺陷的东西卡住了 std::async非常依赖于实现,可能有两种版本的launch::async,一种是将任务启动到新线程中,另一种是使用线程池/任务调度器 但是,根据用于实现std::async的这些变体中的哪一个,用法会有很大的不同 对于基于“线程池”的变体,您可以启动许多小任务,而不必太担心开销,但是,如果其中一个任务在某个点阻塞呢 另一方面,“启动新线程”变体不会遇到阻塞任务的问题

我一直在考虑
std::async
,以及在未来的编译器实现中应该如何使用它。然而,现在我有点被一些感觉像是设计缺陷的东西卡住了

std::async
非常依赖于实现,可能有两种版本的
launch::async
,一种是将任务启动到新线程中,另一种是使用线程池/任务调度器

但是,根据用于实现
std::async
的这些变体中的哪一个,用法会有很大的不同

对于基于“线程池”的变体,您可以启动许多小任务,而不必太担心开销,但是,如果其中一个任务在某个点阻塞呢

另一方面,“启动新线程”变体不会遇到阻塞任务的问题,另一方面,启动和执行任务的开销将非常高

线程池: +低开销-永不阻塞

启动新线程: +用积木很好,-很高的开销

因此,基本上取决于实现,我们使用
std::async
的方式会非常谨慎。如果我们有一个程序可以很好地与一个编译器配合使用,那么它可能在另一个编译器上工作得很糟糕

这是故意的吗?还是我遗漏了什么?你会像我一样认为这是个大问题吗?

在当前规范中,我缺少类似
std::oversubscribe(bool)
的内容,以便在依赖使用
std::async
的情况下实现


<>编辑:我已经阅读了,C++ 11标准文档没有给出关于发送到 STD::AsYNC 的任务是否会被阻止的提示。

< P>我希望实现能启动新线程,并将线程池留给C++的未来版本来规范。有没有使用线程池的实现



MSVC最初使用基于并发运行时的线程池。根据这一点,已被删除。C++规范为实现者留下了一些聪明的地方,但是我认为它没有足够的空间来实现线程池的实现。特别是,我认为规范仍然要求销毁和重建
thread\u local
对象,但是使用ConcRT的线程池将不支持这一点。

std::async
使用
std::launch::async
策略启动的任务将“如同在新线程中运行”,因此线程池并没有得到真正的支持——运行时必须在每次任务执行之间拆除并重新创建所有线程局部变量,这并不简单

这也意味着您可以期望以
std::launch::async
策略启动的任务同时运行。可能会有启动延迟,如果运行的线程比处理器多,则会有任务切换,但它们应该正在运行,而不是仅仅因为一个线程在等待另一个线程而死锁


一个实现可能会选择提供一个扩展,该扩展允许您的任务在线程池中运行,在这种情况下,由该实现来记录语义。

如果我没记错的话,微软在2012年的Going Native大会上宣布,
std::async
将使用Concrt。我不知道gcc和clang有什么计划。MSVC现在确实使用了线程池,我上次检查时,libc++和libstdc++都没有使用池。作为补充:提供了一个非常简单和现实的阻止
std::async
调用的示例。您似乎假设线程池的大小是固定的。实际上,许多线程池都是动态大小的,所以阻塞不是问题。@MooingDuck:TBB和Concrt都没有“动态”大小的线程池。您知道哪些线程池是动态调整大小的?即使你有一个动态大小的线程池,你也会遇到超额订阅的问题,以及跟踪何时添加新线程和何时删除旧线程所需的任何启发式的开销。@ronag:老实说,我最后使用的是Java。我不知道它什么时候创建线程,但我知道如果“最后一个”线程在60秒内未使用,它将被删除。对于增长,我假设如果一个新任务到达并且线程正在使用,将其排队并等待1秒。如果没有线程打开并CPU@MooingDuck:是的,Java和.NET使用某种启发式方法动态调整线程池的大小。我真的不能说太多关于这些性能。然而,我的问题与C++和标准有关,据我所知,没有广泛使用的C++线程池实现,它使用启发式(至于为什么,我不知道)。MSVC的异步使用线程池。我可以想象,当一个线程完成一个“任务”时,它会报告完成情况,然后重置它的线程局部变量,然后等待一个新任务。这意味着用户代码永远不必等待线程本地重置。我不知道MSVC实现的细节,但是标准要求在
未来
准备就绪之前销毁
线程本地
变量。虽然这是真的,线程本地变量必须重新初始化,但标准规定“仿佛在一个新线程中”。似乎是这里的关键点。详细地说,它留下了“线程池”和“新线程”的选择“致实施者。重新初始化线程局部变量应该比创建新线程便宜。重新初始化
thread\u local
变量可能非常重要,尤其是在动态加载模块的情况下。然而,原则上你是对的。