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

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


当前回答

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

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

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

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

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

其他回答

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

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

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

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

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

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内核中是可以互换使用的……

更复杂的是,还有线程本地存储和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.

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

在我最近的LINUX工作中,需要注意的一件事是库。如果您正在使用线程,请确保跨线程使用的所有库都是线程安全的。这让我疼了好几次。值得注意的是,libxml2并不是开箱即用的线程安全的。它可以用线程安全编译,但这不是你用aptitude install得到的。

我不得不同意你所听到的。当我们对我们的集群(xhpl等)进行基准测试时,我们总是通过进程而不是线程获得明显更好的性能。< /轶事>