这个问题与这个问题有些相关:C++11中std:: thread是否被池化? 尽管这两个问题不同,但意图相同:
问题1:仍然有必要使用自己的(或第三方库的)线程池来避免昂贵的线程创建吗?
其他问题的结论是,您不能依赖于std::thread
被池化(可能会或可能不会)。 但是,std::async(launch::async)
似乎更有可能被池化。
我认为这并不是标准规定的,但我认为所有良好的C++11实现都会在线程创建缓慢时使用线程池。只有在创建新线程不费时的平台上,我才希望它们总是生成新线程。
问题2:这只是我的想法,但我没有事实证明。 我很可能错了。 这是一种合理的猜测吗?
最后,我提供了一些示例代码,首先展示了我如何通过async(launch::async)
表达线程创建:
示例1:
thread t([]{ f(); });
// ...
t.join();
变成
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
示例2:火并忘线程
thread([]{ f(); }).detach();
变成
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
问题3:您更喜欢使用async
版本还是thread
版本?
以下内容仅供澄清:
为什么必须将返回值分配给虚拟变量?
不幸的是,当前的C++11标准强制要求您捕获std::async
的返回值,否则析构函数会被执行,直到操作终止。这在某些情况下被认为是标准的错误(例如,Herb Sutter认为如此)。
来自cppreference.com的示例很好地说明了这一点:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
进一步澄清:
我知道线程池可能有其他合法用途,但在这个问题中,我只关心避免昂贵的线程创建成本方面。
我认为还是有一些情况下线程池非常有用,特别是如果你需要更多的资源控制。例如,服务器可能决定仅同时处理固定数量的请求,以保证快速响应时间并增加内存使用的可预测性。在这种情况下,线程池应该是可以的。
线程局部变量也可能是自己的线程池的一个论点,但我不确定它是否在实践中相关:
- 使用
std::thread
创建新线程时,线程局部变量未初始化而开始。也许这不是你想要的。 - 对于由
async
生成的线程,对我来说有点不太清楚,因为线程可能已被重用。据我所知,线程局部变量不能保证被重置,但我可能错了。 - 另一方面,使用自己的(固定大小)线程池,如果确实需要,可以完全控制。
std::async(launch::async)
似乎有更高的被池化的可能性。不,我认为是std::async(launch::async | launch::deferred)
可能会被池化。只使用launch::async
时,任务应该在新线程上启动,而不管其他任务正在运行什么。如果使用launch::async | launch::deferred
策略,则实现可以选择哪个策略,但更重要的是它可以延迟选择哪个策略。也就是说,它可以等待直到线程池中的线程可用,然后选择异步策略。 - bames53std::async()
时使用线程池。我仍然很好奇他们如何在线程池中支持非平凡的thread_local
析构函数。 - bames53launch::async
,则会将其视为仅launch::deferred
,并且从未异步执行 - 因此,在实际效果上,该版本的libstdc++“选择”始终使用deferred,除非被强制执行其他策略。 - doug65536std::async
可以成为性能优化的利器 - 它可以成为标准的短期任务执行系统,并自然地由线程池支持。但现在,它只是一个std::thread
,附带了一些垃圾代码以使线程函数能够返回一个值。还有,他们添加了冗余的“deferred”功能,完全重叠了std::function
的作用。 - doug65536