在处理网络/IO密集型应用程序时,与进程相比,在Linux上工作的线程限制是什么?

30

据我所知,在多核服务器上,当您只有一个进程但有多个线程时,在Linux系统下可能无法达到最佳性能,因为Linux系统对IO有一些限制,因此在8核服务器上运行8线程的1个进程可能比运行8个进程更慢。

还有其他限制可能会减缓应用程序的速度吗?该应用程序是一个网络C++应用程序,为数百个客户端提供服务,并具有一些磁盘IO。

更新: 我担心除了我自己实现的锁之外还存在一些与IO相关的问题... 在多个线程中同时进行网络/磁盘IO不会有任何问题吗?

2个回答

66

线程的缺点

线程:

  • 在内存操作上进行序列化。也就是说,内核和MMU必须处理一些类似mmap()这样的页面分配操作。
  • 共享相同的文件描述符表。对于更改和查找此表中存储的内容(如文件偏移量和其他标志)需要加锁,每次使用此表进行系统调用(例如open()accept()fcntl()等)都需要锁定它以将fd转换为内部文件句柄,并进行更改。
  • 共享某些调度属性。进程会被不断评估以确定它们对系统的负载情况,并相应地进行调度。大量的线程意味着更高的CPU负载,这通常不受调度程序的欢迎,它会增加该进程事件的响应时间(例如在套接字上读取传入的数据)。
  • 可能共享一些可写内存。任何由多个线程写入的内存(尤其是如果它需要复杂的加锁),都会产生各种缓存争用和护航问题。例如,堆操作(如malloc()free())作用于全局数据结构(可以在某种程度上解决)。还有其他全局结构。
  • 共享凭据,这可能是服务类型进程的问题。
  • 共享信号处理,这些将在处理它们时中断整个进程。

进程还是线程?

  • 如果希望使调试更容易,请使用线程。
  • 如果您使用的是Windows,请使用线程。(在Windows中,进程非常重量级。)
  • 如果稳定性是一个巨大的问题,请尝试使用进程。(一次SIGSEGV/PIPE就足以导致问题...)。
  • 如果线程不可用,请使用进程。(现在不太常见,但确实会发生。)
  • 如果您的线程共享无法从多个进程中使用的资源,请使用线程。(或者提供一种IPC机制以允许与资源的“所有者”线程进行通信。)
  • 如果你使用的资源只能每个进程一份(而你需要每个上下文一个),那么就使用进程。
  • 如果处理上下文没有任何共用(例如生成并忘记连接的套接字服务器,只要 accept() 就可以了),且CPU是瓶颈,请使用进程和单线程运行时(它们不包含诸如堆等地方的所有强烈锁定)。
  • 线程和进程之间最大的区别之一是:线程使用软件结构来保护数据结构,而进程使用硬件(速度明显更快)。
  • 链接


    1
    谢谢你的精彩回答,这正是我所需要的。 - BarsMonster
    1
    非常好的回答!您能详细说明一下最后一个要点吗?指的是哪些硬件结构?线程不能使用这些硬件结构实现吗? - kizzx2
    @kizzx2:进程在它们自己的内存空间中运行。这是由CPU中的内存管理单元强制执行的。每个页面访问标志、帧地址等实际上都是由MMU处理的。线程共享内存空间,它们通过与其他线程共享MMU上下文来实现这一点,“保护”是由用户代码(例如提供每个线程数据、互斥锁等)执行的。进程和线程的分离只是标签,实际上存在许多共享程度,特别是clone 2,尤其是CLONE_VM - Matt Joiner
    @David Heffernan:请注意,我的命名法基于广泛的比较和许多心碎。离开黑暗面吧,现在对你来说还不算太晚(我在2006年离开了黑暗面)。 - Matt Joiner
    2
    这是一个非常出色的回答,但使用“Windoze”这个词汇只会贬低它。不管你对Windows有什么看法,它肯定拥有出色的线程API,无论你对其余部分有何看法。 - David Heffernan

    -1

    这实际上应该没有什么区别,但可能与设计有关。

    多进程应用程序可能需要做更少的锁定,但可能会使用更多的内存。在进程之间共享数据可能会更加困难。

    另一方面,多进程可以更加健壮。您可以调用exit()并安全地退出子进程,而不会对其他进程产生影响。

    这取决于客户端的依赖程度。我通常建议选择最简单的解决方案。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接