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

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


当前回答

线程/进程之间的决定取决于您将使用它来做什么。 进程的好处之一是它有一个PID,可以在不终止父进程的情况下被杀死。

对于一个真实世界的web服务器的例子,apache 1.3过去只支持多进程,但是在2.0中他们增加了一个抽象,这样你就可以在两者之间切换。评论似乎同意进程更健壮,但线程可以提供更好的性能(除了那些进程性能很差且您只想使用线程的窗口)。

其他回答

如果你需要共享资源,你真的应该使用线程。

还要考虑这样一个事实:线程之间的上下文切换比进程之间的上下文切换代价要小得多。

我认为没有理由明确地使用单独的进程,除非你有一个很好的理由这样做(安全,经过验证的性能测试,等等……)

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

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

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

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内核文档

Linux使用1-1线程模型,(对内核来说)没有进程和线程的区别——一切都只是一个可运行的任务。*

在Linux上,系统调用clone克隆一个任务,共享级别可配置,其中包括:

CLONE_FILES:共享相同的文件描述符表(而不是创建一个副本) CLONE_PARENT:不要在新任务和旧任务之间建立父子关系(否则,child的getppid() = parent的getpid()) CLONE_VM:共享相同的内存空间(而不是创建COW副本)

Fork()调用clone(共享最少),pthread_create()调用clone(共享最多)。**

fork的成本比pthread_creation略高,因为需要复制表并为内存创建COW映射,但是Linux内核开发人员已经尝试(并成功)将这些成本最小化。

如果任务共享相同的内存空间和不同的表,那么它们之间的切换将比不共享的任务稍微便宜一些,因为数据可能已经加载到缓存中了。然而,即使没有任何共享,切换任务仍然非常快——这是Linux内核开发人员试图确保(并成功确保)的另一件事。

事实上,如果您在多处理器系统上,不共享实际上可能有利于性能:如果每个任务都在不同的处理器上运行,同步共享内存的成本很高。


*简化。CLONE_THREAD导致信号传递被共享(这需要共享信号处理程序表的CLONE_SIGHAND)。

* *简化。SYS_fork和SYS_clone系统调用都存在,但是在内核中,SYS_fork和SYS_clone都是对同一个do_fork函数的非常薄的包装,而do_fork函数本身也是对copy_process的薄包装。是的,进程、线程和任务这三个术语在Linux内核中是可以互换使用的……

如果您希望尽可能地创建一个纯a进程,您可以使用clone()并设置所有克隆标志。(或者调用fork()来节省打字的时间)

如果你想创建一个纯粹的线程,你可以使用clone()并清除所有的clone标志(或者节省你自己的输入工作并调用pthread_create())

有28个标志指示资源共享的级别。这意味着你可以创建超过2.68亿种类型的任务,这取决于你想分享什么。

这就是我们所说的Linux不区分进程和线程,而是指程序中的任何控制流都是任务的意思。不区分两者的理由是,嗯,并不是唯一定义了超过2.68亿种口味!

因此,做出是使用进程还是线程的“完美决定”实际上就是决定克隆28种资源中的哪一种。