请问在C ++ 11化妆线程池异步(启动::异步)过时避免昂贵的创建线程?(Does async(l

2019-07-18 06:39发布

它是松散的与此相关的问题: 是否标准::线程池在C ++ 11? 。 虽然问题不同,目的是一样的:

问题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 ,否则执行析构函数,该块,直到行动结束。 它是由一些在标准认为是错误的(例如,通过香草萨特)。

从这个例子cppreference.com说明好听:

{
  std::async(std::launch::async, []{ f(); });
  std::async(std::launch::async, []{ g(); });  // does not run until f() completes
}

另一个澄清:

我知道, 线程池可能有其他正当用途,但在这个问题上我只是在避免了昂贵的线程创建成本方面感兴趣

我认为仍有一些线程池是非常有用的,特别是如果你需要对资源更多的控制情况。 例如,服务器可能会决定同时处理只请求固定数量,以保证快速的响应时间,提高内存的使用情况的预测性。 线程池应该没事,在这里。

线程局部变量也可能是你自己的线程池的说法,但我不知道它是否是相关的实践:

  • 创建一个新的线程std::thread启动,但不初始化线程局部变量。 也许这是不是你想要的。
  • 在由产生的线程async ,它是有点不清楚我,因为线程可能被重复使用。 从我的理解,线程局部变量并不能保证被重置了,但我可能是错误的。
  • 使用你自己的(固定大小)的线程池,而另一方面,让您完全控制,如果你真的需要它。

Answer 1:

问题1:

我改变了这种从原因为原来是错的。 我的印象是, Linux的线程的创建是非常便宜和测试后,我确定函数调用的一个新的线程与一个正常的开销是巨大的。 创建一个线程来处理函数调用的开销是一样的东西10000倍以上普通函数调用速度较慢。 所以,如果你发出了很多小函数调用,一个线程池可能是一个好主意。

这是相当明显的是,附带的g ++没有标准的C ++库的线程池。 但我可以明确地看到他们的情况。 即使不得不推通过某种线程间队列调用的开销,这可能比启动一个新的线程便宜。 而标准允许这样做。

恕我直言,Linux内核的人应该做的线程创建价格比它当前的工作。 但是,C ++标准库还应该考虑使用池来实现launch::async | launch::deferred launch::async | launch::deferred

而OP是正确的,使用::std::thread发动当然线程迫使一个新的线程,而不是使用一个从池的创建。 所以::std::async(::std::launch::async, ...)是首选。

问题2:

是的,基本上这“隐含”启动一个线程。 但实际上,它仍然是相当明显发生了什么。 所以,我真的不认为这个词隐含是一个特别好的词。

我也并不认为强迫你等待返回之前的破坏必然是一个错误。 我不知道,你应该使用async调用,以创建不应该会返回“守护”线程。 如果他们被预期的回报,这不是确定是忽略例外。

问题3:

就个人而言,我喜欢线程启动是明确的。 我把岛屿上,你可以保证按顺序访问了很多价值。 否则,你最终与你总是要围绕地方包裹一个互斥体,并记住使用它可变的状态。

我喜欢的工作队列模型一大堆比“未来”的模式更好,因为有躺在附近,因此您可以更有效地处理可变状态“的串行孤岛”。

但实际上,这取决于你在做什么。

性能测试

所以,我测试的呼唤东西各种方法的性能和一个8核与这些数字上来(AMD Ryzen 7 2700X)运行Fedora 29系统铛版本7.0.1)和libc(++(没有的libstdc ++)编译:

   Do nothing calls per second:   35365257                                      
        Empty calls per second:   35210682                                      
   New thread calls per second:      62356                                      
 Async launch calls per second:      68869                                      
Worker thread calls per second:     970415                                      

与本土的,在我的MacBook Pro 15" (英特尔(R)酷睿(TM)i7-7820HQ CPU @ 2.90GHz)与Apple LLVM version 10.0.0 (clang-1000.10.44.4)下OSX 10.13.6,我得到这样的:

   Do nothing calls per second:   22078079
        Empty calls per second:   21847547
   New thread calls per second:      43326
 Async launch calls per second:      58684
Worker thread calls per second:    2053775

对于工作线程,我开始了一个线程,然后用无锁队列发送请求到另一个线程,然后等待“就是这样”答复被送回。

“不作为”是只是为了测试测试工具的开销。

很明显,启动一个线程的开销是巨大的。 甚至与线程间队列工作者线程会减慢速度由20个左右的在Fedora 25在虚拟机中的一个因素,并通过本地OS X.约8

我创建了一个项目到位桶抱着我用于性能测试的代码。 它可以在这里找到: https://bitbucket.org/omnifarious/launch_thread_performance



文章来源: Does async(launch::async) in C++11 make thread pools obsolete for avoiding expensive thread creation?