创建线程时有多少开销?

jdt*_*141 37 c++ posix pthreads

我刚刚回顾了一些非常糟糕的代码 - 通过创建一个新线程来在串行端口上发送消息的代码,以便在发送的每条消息的新线程中打包和组装消息.是的,对于创建pthread的每条消息,正确设置了位,然后线程终止.我不知道为什么有人会做这样的事情,但它提出了一个问题 - 实际创建一个线程有多少开销?

Naf*_*aus 42

为了复活这个旧线程,我只做了一些简单的测试代码:

#include <thread>

int main(int argc, char** argv)
{
  for (volatile int i = 0; i < 500000; i++)
    std::thread([](){}).detach();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我编译了它g++ test.cpp -std=c++11 -lpthread -O3 -o test.然后我在旧的(内核2.6.18)负载很重(做数据库重建)慢速笔记本电脑(英特尔酷睿i5-2540M)上连续运行了三次.连续三次运行的结果:5.647s,5.515s和5.561s.所以我们在这台机器上看到每个线程超过10微秒,可能更少在你的机器上.

考虑到串行端口最大速度大约为每10微秒1位,这根本没有太大的开销.现在,当然可以获得涉及传递/捕获参数的各种额外线程丢失(尽管函数调用本身可以强加一些),核心之间的缓存减速(如果不同核心上的多个线程同时在同一内存上进行争夺),但总的来说,我非常怀疑你提出的用例会对性能产生负面影响(并且可能会带来好处,取决于),尽管你已经先发制人地将这个概念标记为"非常糟糕的代码"而不知道需要多少时间发起一个线程.

这是否是一个好主意在很大程度上取决于你的情况的细节.还有什么是调用线程负责?准备和写出数据包究竟涉及什么?他们写出的频率(具有什么样的分布?统一,聚集等等?)以及它们的结构是什么样的?系统有多少个核心?等等.根据细节,最佳解决方案可以是从"根本没有线程"到"共享线程池"到"每个数据包的线程"的任何地方.

请注意,线程池不是魔术,并且在某些情况下可能是对独特线程的减速,因为线程最大的减速之一是同时同步多个线程使用的高速缓存内存,而线程池本质上是从不同的线程中查找和处理更新必须这样做.因此,如果处理器不确定其他进程是否更改了一段内存,那么主线程或子处理线程可能会被迫等待.相比之下,在理想情况下,给定任务的唯一处理线程只需与其调用任务共享一次内存(当它启动时),然后它们再也不会相互干扰.

  • 切线:遇到该线程时,作为Windows用户,我很好奇系统的运行状况。在msvc下使用标准发行版优化进行编译,在6700k上运行需要31.442s才能完全运行。我所做的唯一更改是在循环之前和之后添加了std :: chrono :: high_resolution_clock + time_points,并在退出前std :: cout结果。令人震惊的结果。我用您的确切命令行参数尝试了mingw-w64的7.1.0 g ++,但是它在几秒钟后崩溃了,所以不知道那里出了什么问题,就像我躺在的clang ++ v8.0一样。 (2认同)

ubi*_*con 11

我一直被告知线程创建很便宜,特别是与创建进程的替代方法相比.如果你正在谈论的程序没有很多需要同时运行的线程,然后可能没有必要的操作,并通过你写这很可能是这样的判断.一些文献支持我:

http://www.personal.kent.edu/~rmuhamma/OpSystems/Myos/threads.htm

线程在某种意义上是便宜的

  1. 因此,它们只需要堆栈和存储寄存器,线程创建起来很便宜.

  2. 线程使用它们正在运行的操作系统的非常少的资源.也就是说,线程不需要新的地址空间,全局数据,程序代码或操作系统资源.

  3. 使用线程时,上下文切换很快.原因是我们只需要保存和/或恢复PC,SP和寄存器.

这里有更多相同的内容.

操作系统概念第8版(第155页)中,作者写了有关线程的好处:

为流程创建分配内存和资源的成本很高. 因为线程共享它们所属的进程的资源,所以创建和上下文切换线程更经济.经验性地衡量开销的差异可能很困难,但一般来说,创建和管理进程比使用线程要花费更多时间.例如,在Solaris中,创建进程的速度比创建线程慢大约三十倍,而上下文切换速度大约慢五倍.

  • 但替代方案可能是单个重用线程,或线程池,而不是进程. (5认同)
  • 实际上进程创建比线程创建便宜。进程创建的 fork 部分基本上没有成本,因为内存页面在硬件级别复制。看看谷歌浏览器团队发现了什么:http://www.hanselman.com/blog/MicrosoftIE8AndGoogleChromeProcessesAreTheNewThreads.aspx (3认同)

rus*_*lik 10

有一个在线程创建一些开销,但它与串行端口通常是缓慢的波特率(19200位/秒是最常见的)比较,它只是无所谓.

  • 我同意.当网络可能导致数十毫秒甚至几秒的延迟时,为什么要担心创建线程的延迟微秒. (3认同)

Mic*_*eyn 10

你绝对不想这样做.创建单个线程或线程池,并在消息可用时发出信号.在接收到信号后,线程可以执行任何必要的消息处理.

就开销而言,线程创建/销毁(尤其是在Windows上)相当昂贵.大概在几十微秒的某个地方,具体而言.它应该在大多数情况下仅在应用程序的开始/结束时完成,可能的例外是动态调整大小的线程池.

  • 是的,"永恒的"专用工作线程也可以解决可能的MT问题. (3认同)

Ton*_*roy 8

...在串行端口上发送消息...对于创建pthread的每条消息,正确设置位,然后线程终止.......实际创建线程时有多少开销?

这是高度系统特定的.例如,上次我使用VMS线程是噩梦般缓慢(多年来,但是从内存中,一个线程可以创建每秒10个以上的东西(如果你保持这种状态几秒钟没有线程退出你的核心),而在Linux上你可能会创造数千个.如果您想确切地知道,请在您的系统上进行基准测试.但是,仅仅知道更多关于消息的信息并不多见:它们是平均5个字节还是100k,它们是连续发送还是线路闲置,以及应用程序的延迟要求都是相关的代码的线程使用的适当性作为线程创建开销的任何绝对度量.并且性能可能不需要成为主要的设计考虑因素.


小智 7

我在我制作的 VOIP 应用程序中使用了上述“糟糕”的设计。它工作得非常好......对于本地连接的计算机,绝对没有延迟或丢失/丢失数据包。每次数据包到达时,都会创建一个线程并将该数据交给输出设备进行处理。当然,数据包很大,因此不会造成瓶颈。同时主线程可以循环回等待并接收另一个传入的数据包。

我已经尝试了其他设计,其中我需要的线程是预先创建的,但这会产生它自己的问题。首先,您需要为线程正确设计代码以检索传入的数据包并以确定性的方式处理它们。如果您使用多个(预先分配的)线程,则数据包可能会被“乱序”处理。如果您使用单个(预先分配的)线程来循环并接收传入的数据包,则线程可能会遇到问题并终止,从而没有线程处理任何数据。

创建一个线程来处理每个传入的数据包工作得非常干净,尤其是在多核系统和传入数据包很大的情况下。同样为了更直接地回答您的问题,创建线程的替代方法是创建一个管理预分配线程的运行时进程。能够同步数据切换和处理以及检测错误可能会增加与仅仅创建新线程一样多的开销,如果不是更多的话。这一切都取决于您的设计和要求。


Lun*_*oms 6

为了进行比较,请查看 OSX:链接

  • 内核数据结构:大约 1 KB 堆栈空间:512 KB(辅助线程):8 MB(OS X 主线程),1 MB(iOS 主线程)

  • 创建时间:约 90 微秒

posix 线程的创建也应该围绕这个(不是一个遥远的数字)我猜。


Mar*_*oon 5

线程中的线程创建和计算非常昂贵。需要设置所有数据结构,向内核注册的线程和线程切换必须发生,以便新线程实际执行(在未指定和不可预测的时间)。执行 thread.start 并不意味着立即调用线程 main 函数。正如文章(通过拼写提到的)指出的,线程的创建仅比进程的创建便宜。总的来说,它是相当昂贵的。

我永远不会使用线程

  • 一个简短的计算
  • 在我的代码流中需要结果的计算(这意味着,我正在启动线程并等待它返回它的计算结果

在您的示例中,创建一个处理所有串行通信并且是永恒的线程是有意义的(正如已经指出的那样)。

马里奥