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

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


当前回答

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

其他回答

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

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

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

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

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

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

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