存储如何与 std::future 分配相关联?

How is the storage associated with std::future allocated?

本文关键字:分配 关联 future 存储 std      更新时间:2023-10-16

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

int foo()
{
return 42;
}
...
std::future<int> x = std::async(foo);

在此示例中,如何分配x异步状态的存储,以及哪个线程(如果涉及多个线程)负责执行分配?此外,std::async的客户对分配有任何控制权吗?

对于上下文,我看到std::promise的一个构造函数可能会收到一个分配器,但我不清楚是否可以在std::async级别自定义std::future的分配。

内存由调用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 & launch::async不为零 — 调用INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)(20.8.2, 30.3.1.2) 就像在由线程对象表示的新执行线程中一样,对DECAY_COPY ()的调用正在调用async的线程中进行评估。

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

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

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

为了更直接地控制异步调用的行为,还有std::packaged_task,它确实有一个分配器参数。但是从单纯的标准报价来看,这个分配器是否仅用于为函数分配存储(因为std::packaged_task是一种特殊的std::function),或者它是否也用于分配内部std::promise的共享状态,尽管似乎有可能:

30.6.9.1 [期货任务成员]:

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

好吧,它甚至没有说下面有一个std::promise(同样适用于std::async),它可能只是一个可连接到std::future的未定义类型。

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

但实际上,这可能是规范中的疏忽。虽然分配控制并不适合std::async,但std::packaged_task及其对分配器的使用的解释可能更清楚一些。但这也可能是有意为之的,因此std::packaged_task可以自由使用任何它想要的东西,甚至不需要内部std::promise

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