3

As I understand it, usual implementations of std::async schedule these jobs on threads from a pre-allocated thread pool.

So lets say I first create and schedule enough long-running std::asyncs to keep all threads from that thread pool occupied. Directly afterwards (before long they finished executing) I also create and schedule some short-running std::asyncs. Could it happen that the short-running ones aren't executed at all until at least one of the long-running ones has finished? Or is there some guarantee in the standard (specifically C++11) that prevents this kind of situation (like spawning more threads so that the OS can schedule them in a round-robin fasion)?

4

2 回答 2

2

标准如下:

[ futures.async#3.1 ] 如果在策略中设置了 launch​::​async,则调用INVOKE(DECAY_­COPY(std​::​forward<F>(f)), DECAY_­COPY(std​::​forward<Args>(args))...)([func.require], [thread.thread.constr])就像在一个由线程对象表示的新执行线程中一样在调用异步的线程中评估对 DECAY_COPY 的调用。[...]

因此,在as-if规则下,必须在使用启动策略调用时产生新线程。当然,实现可能会在内部使用线程池,但除了通常的线程创建开销之外,不会发生特殊的“饥饿”。此外,像线程局部变量的初始化这样的事情应该总是发生。async()​async

实际上,clang libc++ trunk async implementation 的内容如下:

unique_ptr<__async_assoc_state<_Rp, _Fp>, __release_shared_count>
        __h(new __async_assoc_state<_Rp, _Fp>(_VSTD::forward<_Fp>(__f)));

VSTD::thread(&__async_assoc_state<_Rp, _Fp>::__execute, __h.get()).detach();

return future<_Rp>(__h.get());

如您所见,内部没有使用“显式”线程池。

此外,您也可以在此处阅读gcc 5.4.0 附带的 libstdc++ 实现只是调用一个普通线程。

于 2017-11-30T13:34:47.213 回答
0

是的,至少从 MSVC2015 开始,MSVCstd::async似乎完全具有该属性。

我不知道他们是否在 2017 年的更新中修复了它。

这违背了标准的精神。但是,该标准对线程前向进度保证非常模糊(至少从 C++14 开始)。因此,虽然std::async它必须表现得好像它包装了 a std::thread,但对std::thread向前进展的保证足够弱,以至于在 as-if 规则下这并不是什么保证。

在实践中,这导致我std::async在我的线程池实现中用对 的原始调用来替换std::thread,因为在 MSVC2015 中的原始使用std::thread似乎没有这个问题。

我发现线程池(带有任务队列)比对 or 的原始调用要实用得多,并且由于使用std::asyncorstd::thread编写线程池非常容易std::threadstd::async我建议使用std::thread.

您的线程池可以std::future像返回一样返回 s std::async(但没有破坏时自动阻塞功能,因为池本身管理线程生命周期)。

我已经读过 C++17 增加了更好的前进进度保证,但我缺乏足够的理解来断定 MSVC 的行为现在是否违反标准要求。

于 2017-11-30T15:36:37.713 回答