最近我听到一些人说,在Linux中,使用进程几乎总是比使用线程更好,因为Linux在处理进程方面非常高效,而且与线程相关的问题太多了(比如锁)。然而,我对此持怀疑态度,因为在某些情况下,线程似乎可以带来相当大的性能提升。

因此,我的问题是,当遇到线程和进程都可以很好地处理的情况时,我应该使用进程还是线程?例如,如果我正在编写一个web服务器,我应该使用进程还是线程(或组合)?


当前回答

I think everyone has done a great job responding to your question. I'm just adding more information about thread versus process in Linux to clarify and summarize some of the previous responses in context of kernel. So, my response is in regarding to kernel specific code in Linux. According to Linux Kernel documentation, there is no clear distinction between thread versus process except thread uses shared virtual address space unlike process. Also note, the Linux Kernel uses the term "task" to refer to process and thread in general.

没有实现进程或线程的内部结构,而是有一个结构体task_struct,它描述了一个称为task的抽象调度单元。

另外,根据Linus Torvalds的说法,你根本不应该考虑进程和线程,因为这太有限了,唯一的区别是COE或执行上下文在“从父地址空间分离”或共享地址空间方面的区别。事实上,他在这里用了一个web服务器的例子来说明他的观点(强烈推荐阅读)。

完全归功于linux内核文档

其他回答

这取决于很多因素。进程比线程更重,启动和关闭成本更高。进程间通信(IPC)也比线程间通信更困难、更慢。

相反,进程比线程更安全,因为每个进程都运行在自己的虚拟地址空间中。如果一个进程崩溃或缓冲区溢出,它根本不会影响任何其他进程,而如果一个线程崩溃,它会关闭进程中的所有其他线程,如果一个线程缓冲区溢出,它会在所有线程中打开一个安全漏洞。

因此,如果应用程序的模块可以在很少通信的情况下独立运行,那么如果能够负担得起启动和关闭成本,则可能应该使用进程。IPC对性能的影响将是最小的,并且您在漏洞和安全漏洞方面会稍微安全一些。如果您需要获得或拥有大量共享数据(例如复杂的数据结构),那么请使用线程。

在大多数情况下,我更喜欢进程而不是线程。 当您有一个相对较小的任务(每个划分的任务单元占用的进程开销>>时间),并且需要在它们之间共享内存时,线程可能会很有用。想象一个大数组。 另外(离题),请注意,如果您的CPU利用率是100%或接近100%,那么多线程或处理将没有任何好处。(事实上情况会更糟)

从前有Unix,在这个好的旧Unix中,进程有很多开销,所以一些聪明的人所做的是创建线程,这些线程将与父进程共享相同的地址空间,他们只需要减少上下文切换,这将使上下文切换更有效。

在当代的Linux (2.6.x)中,进程的上下文切换和线程的上下文切换在性能上没有太大的区别(只有MMU的东西对线程来说是额外的)。 共享地址空间存在问题,这意味着线程中的错误指针可能破坏同一地址空间中的父进程或另一个线程的内存。

一个进程受到MMU的保护,所以一个错误的指针只会导致一个信号11,而不会损坏。

我通常会使用进程(在Linux中没有太多的上下文切换开销,但是由于MMU的内存保护),但是如果我需要一个实时调度器类,则会使用pthreads,这完全是另一回事。

为什么你认为线程在Linux上有这么大的性能提升?你有这方面的数据吗,还是说这只是一个神话?

其他人讨论了这些考虑因素。

也许重要的区别是,在Windows中,与线程相比,进程是沉重和昂贵的,而在Linux中,差异要小得多,所以等式在不同的点上平衡。

更复杂的是,还有线程本地存储和Unix共享内存。

Thread-local storage allows each thread to have a separate instance of global objects. The only time I've used it was when constructing an emulation environment on linux/windows, for application code that ran in an RTOS. In the RTOS each task was a process with it's own address space, in the emulation environment, each task was a thread (with a shared address space). By using TLS for things like singletons, we were able to have a separate instance for each thread, just like under the 'real' RTOS environment.

共享内存(显然)可以为您带来让多个进程访问相同内存的性能优势,但代价是必须正确地同步进程。一种方法是让一个进程在共享内存中创建一个数据结构,然后通过传统的进程间通信(如命名管道)向该结构发送句柄。