它与这个问题有着松散的联系:std::thread是否在C11中合并?。虽然问题不同,但意图是一样的:
问题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:比起线程
版本,您更喜欢异步
版本吗?
其余部分不再是问题的一部分,只是为了澄清:
为什么必须将返回值指定给虚拟变量?
不幸的是,当前的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
创建新线程时,不会启动初始化的线程局部变量。也许这不是你想要的
问题一:
我改变了原来的版本,因为原来的版本是错误的。我的印象是Linux线程创建非常便宜,经过测试,我确定新线程中函数调用的开销与普通线程相比是巨大的。创建一个线程来处理函数调用的开销比普通函数调用慢10000倍或更多倍。所以,如果你发出很多小函数调用,线程池可能是个好主意。
很明显,与g一起提供的标准C库没有线程池。但我绝对可以为它们找到一个案例。即使必须将调用推入某种线程间队列的开销,它也可能比启动新线程更便宜。标准允许这样做。
我的意思是,Linux内核人员应该努力使线程创建比目前更便宜。但是,标准C库也应该考虑使用池来实现launch::async | launch::deferred
。
OP是正确的,使用::std::线程
启动线程当然会强制创建一个新线程,而不是使用池中的线程。因此::std::async(::std::启动::async,...)
是首选。
问题2:
是的,基本上这个“隐式”启动一个线程。但事实上,发生的事情仍然很明显。所以我并不认为这个词含蓄地是一个特别好的词。
我也不认为强迫你在毁灭之前等待返回一定是错误的。我不知道您是否应该使用async
调用来创建预期不会返回的“守护进程”线程。如果他们被期望返回,那么忽略异常是不好的。
问题3:
就个人而言,我喜欢线程启动是明确的。我非常重视可以保证串行访问的孤岛。否则你最终会得到可变状态,你总是必须在某个地方包装一个互斥锁并记住使用它。
我喜欢工作队列模型比“未来”模型好得多,因为周围有“串行岛”,因此您可以更有效地处理可变状态。
但实际上,这完全取决于你在做什么。
因此,我测试了各种调用方法的性能,并在运行Fedora 29的8核(AMD Ryzen 7 2700X)系统上提出了这些数字,该系统使用clang版本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)与苹果LLVM版本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
对于工作线程,我启动了一个线程,然后使用一个无锁队列将请求发送到另一个线程,然后等待“完成”回复被发送回来。
“什么都不做”只是测试测试线束的头顶。
很明显,启动线程的开销是巨大的。在VM中的Fedora 25上,即使是具有线程间队列的工作线程也会将速度减慢20倍左右,在本机OS X上减慢约8倍。
我创建了一个OSDN室,其中保存了我用于性能测试的代码。可以在这里找到:https://osdn.net/users/omnifarious/pf/launch_thread_performance/
我是TPL的新手,我想知道:C#5.0新增的异步编程支持(通过新的异步和等待关键字)与线程的创建有什么关系? 具体来说,每次使用异步/等待是否都会创建一个新线程?如果有许多嵌套方法使用异步/等待,那么是否为这些方法中的每一个都创建了一个新线程?
使用 时,线程池中最初创建了多少线程,Javadoc 没有指定任何数字,是否有一个保证的数字,我们最初总是会得到 10 个或其他什么。文档如下: newCachedThreadPool public static ExecutorService newCachedThreadPool()创建一个线程池,该线程池根据需要创建新的线程,但会在以前构建的线程可用时重用它们。这些池通常会提高执行许多短期异
问题内容: 创建线程很昂贵。但是为什么价格昂贵呢?当创建Java线程使创建过程变得昂贵时,究竟发生了什么?我认为该说法是正确的,但是我只是对JVM中的线程创建机制感兴趣。 线程生命周期开销。线程创建和拆除不是免费的。实际开销因平台而异,但是线程创建会花费时间,从而在请求处理中引入延迟,并且需要JVM和OS进行某些处理活动。如果请求是频繁且轻量的(如在大多数服务器应用程序中一样),则为每个请求创建一
如果我有一个固定大小的线程池,它什么时候真正调用启动线程?(它会在创建时启动它们吗?还是等到我开始提交任务时再启动?)
问题内容: 基本上我需要在更多线程中运行〜数百个计算。我只想在paralell中运行一些并行线程,例如5个线程和5个计算。 我正在使用spring框架,@Async选项是自然选择。我不需要全功能的JMS队列,这对我来说有点麻烦。 有任何想法吗 ?谢谢 问题答案: 你检查了吗?你可以定义一个线程池,其中包含最大数量的线程来执行任务。 如果要与结合使用,请在spring-config中使用它:
我对最新gcc中基于pthread和Ubuntu开发环境的线程的互斥锁和消息传递的性能感兴趣。一个很好的通用问题是用餐哲学家,每个哲学家使用lh和rh叉子与左右手邻居共享。我把哲学家的数量增加到99个,让我的四核处理器保持忙碌。 上面的代码允许我的哲学家尝试抓住他们需要的两个叉子。 上面的代码监控我的哲学家的进食或思考进度,这取决于他们是否能够保留这两个叉子。 在所有哲学家尝试自由选择后,等待所有