【问题标题】:How is the storage associated with std::future allocated?与 std::future 关联的存储是如何分配的?
【发布时间】:2012-10-11 07:24:34
【问题描述】:

获得std::future 的一种方法是通过std::async

int foo()
{
  return 42;
}

...

std::future<int> x = std::async(foo);

在这个例子中,x 的异步状态的存储是如何分配的,哪个线程(如果涉及多个线程)负责执行分配?此外,std::async 的客户是否可以控制分配?

对于上下文,我看到std::promiseone of the constructors 可能会收到分配器,但我不清楚是否可以在std::async 级别自定义std::future 的分配。

【问题讨论】:

    标签: c++ asynchronous c++11 future c++-standard-library


    【解决方案1】:

    内存是由调用std::async的线程分配的,你无法控制它是如何完成的。通常它将由new __internal_state_type 的某些变体完成,但不能保证;它可以使用malloc,或者专门为此目的选择的分配器。

    从 30.6.8p3 [futures.async]:

    "效果:第一个函数的行为与调用第二个函数的行为相同,策略参数为 launch::async | launch::deferredFArgs 的参数相同。第二个函数创建一个共享状态,该状态关联与返回的未来对象。..."

    “第一个函数”是没有启动策略的重载,而第二个是有启动策略的重载。

    std::launch::deferred的情况下,没有其他线程,所以一切都必须发生在调用线程上。对于std::launch::async,30.6.8p3 继续说:

    ——如果policy &amp; launch::async 非零——调用INVOKE (DECAY_COPY (std::forward&lt;F&gt;(f)), DECAY_COPY (std::forward&lt;Args&gt;(args))...) (20.8.2, 30.3.1.2) 就像在一个由线程对象表示的新执行线程中一样DECAY_COPY () 的调用是在调用 async 的线程中进行评估。 ...

    我已经添加了重点。由于函数和参数的复制必须发生在调用线程中,这本质上要求共享状态由调用线程分配。

    当然,您可以编写一个实现来启动新线程,等待它分配状态,然后返回引用它的future,但您为什么要这样做?

    【讨论】:

    • 哪里保证调用线程分配存储?好的,很有可能,但我仍然希望看到标准的引用。
    • 更新了更多细节的答案。
    • 确实,看起来很清楚,我忽略了这一点。但是,像现在这样在您的回答中指出这一点不会造成任何伤害。谢谢和 +1。
    • @AnthonyWilliams 用户可能关心分配存储的线程的一个原因是局部性。如果std::async 创建的线程在可以更快访问特定内存区域的内核上执行,那么在该区域中定位异步状态是有意义的。
    【解决方案2】:

    仅从std::async 的参数来看,似乎无法控制内部std::promise 的分配,因此它可以使用任何东西,尽管可能是std::allocator。虽然我猜理论上它是未指定的,但共享状态很可能是在调用线程内分配的。我没有在标准中找到任何关于这个问题的明确信息。最后,std::async 是一个非常专业的工具,可以轻松进行异步调用,因此您不必考虑是否真的 std::promise 任何地方。

    为了更直接地控制异步调用的行为,还有std::packaged_task,它确实有一个分配器参数。但仅从标准引用来看,这个分配器是否仅用于为函数分配存储空间(因为std::packaged_task 是一种特殊的std::function)或者它是否也用于分配内部std::promise,虽然看起来很可能:

    30.6.9.1 [futures.task.members]:

    效果: 构造一个具有共享状态的新 packaged_task 对象,并且 使用std::forward&lt;F&gt;(f) 初始化对象的存储任务。这 采用Allocator 参数的构造函数使用它来分配内存 需要存储内部数据结构。

    好吧,它甚至没有说下面有std::promisestd::async 也是如此),它可能只是一个可连接到std::future 的未定义类型。

    因此,如果确实没有指定std::packaged_task 如何分配其内部共享状态,那么最好的办法可能是实现自己的异步函数调用设施。考虑到,简单地说,std::packaged_task 只是与std::promisestd::async 捆绑在一起的std::function 只是在新线程中启动std::packaged_task(好吧,除非它没有),这应该'问题不大。

    但这确实可能是规范中的疏忽。虽然分配控制并不真正适合std::async,但std::packaged_task 的解释及其对分配器的使用可能会更清楚一些。但这也可能是故意的,所以std::packaged_task 可以随意使用,甚至不需要在内部使用std::promise

    编辑:再读一遍,我认为上面的标准引用确实表明,std::packaged_task 的共享状态使用提供的分配器分配的,因为它是 “内部数据结构” 的一部分,不管它们是什么(虽然不需要是实际的 std::promise)。所以我认为std::packaged_task 应该足以显式控制异步任务的std::future 的共享状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-06
      • 2021-06-02
      • 1970-01-01
      • 2016-04-27
      • 2011-07-26
      • 1970-01-01
      • 2019-02-02
      • 2016-03-28
      相关资源
      最近更新 更多