最近我听到一些人说,在Linux中,使用进程几乎总是比使用线程更好,因为Linux在处理进程方面非常高效,而且与线程相关的问题太多了(比如锁)。然而,我对此持怀疑态度,因为在某些情况下,线程似乎可以带来相当大的性能提升。
因此,我的问题是,当遇到线程和进程都可以很好地处理的情况时,我应该使用进程还是线程?例如,如果我正在编写一个web服务器,我应该使用进程还是线程(或组合)?
最近我听到一些人说,在Linux中,使用进程几乎总是比使用线程更好,因为Linux在处理进程方面非常高效,而且与线程相关的问题太多了(比如锁)。然而,我对此持怀疑态度,因为在某些情况下,线程似乎可以带来相当大的性能提升。
因此,我的问题是,当遇到线程和进程都可以很好地处理的情况时,我应该使用进程还是线程?例如,如果我正在编写一个web服务器,我应该使用进程还是线程(或组合)?
当前回答
你的任务有多紧密耦合?
如果它们可以彼此独立,那么就使用流程。如果它们相互依赖,则使用线程。这样,您就可以终止并重新启动坏进程,而不会影响其他任务的操作。
其他回答
如果您希望尽可能地创建一个纯a进程,您可以使用clone()并设置所有克隆标志。(或者调用fork()来节省打字的时间)
如果你想创建一个纯粹的线程,你可以使用clone()并清除所有的clone标志(或者节省你自己的输入工作并调用pthread_create())
有28个标志指示资源共享的级别。这意味着你可以创建超过2.68亿种类型的任务,这取决于你想分享什么。
这就是我们所说的Linux不区分进程和线程,而是指程序中的任何控制流都是任务的意思。不区分两者的理由是,嗯,并不是唯一定义了超过2.68亿种口味!
因此,做出是使用进程还是线程的“完美决定”实际上就是决定克隆28种资源中的哪一种。
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内核中是可以互换使用的……
Linux(实际上还有Unix)为您提供了第三种选择。
选项1 -流程
创建一个独立的可执行文件来处理应用程序的某些部分(或所有部分),并为每个进程分别调用它,例如,程序运行自己的副本来委托任务。
选项2 -线程
创建一个独立的可执行文件,它由一个线程启动,并创建额外的线程来执行一些任务
选项3 -分叉
仅在Linux/Unix下可用,这有点不同。fork进程实际上是拥有自己地址空间的进程——子进程(通常)无法影响其父进程或兄弟进程的地址空间(不像线程)——因此您获得了额外的健壮性。
但是,内存页不是复制的,它们是写时复制的,因此通常使用的内存比您想象的要少。
考虑一个web服务器程序,它包含两个步骤:
读取配置和运行时数据 服务页面请求
如果您使用线程,第1步将完成一次,第2步将在多个线程中完成。如果您使用“传统”流程,那么每个流程都需要重复步骤1和步骤2,存储配置和运行时数据的内存也需要重复。如果您使用fork(),那么您可以执行第1步,然后fork(),将运行时数据和配置保留在内存中,不受影响,不复制。
所以实际上有三种选择。
这取决于很多因素。进程比线程更重,启动和关闭成本更高。进程间通信(IPC)也比线程间通信更困难、更慢。
相反,进程比线程更安全,因为每个进程都运行在自己的虚拟地址空间中。如果一个进程崩溃或缓冲区溢出,它根本不会影响任何其他进程,而如果一个线程崩溃,它会关闭进程中的所有其他线程,如果一个线程缓冲区溢出,它会在所有线程中打开一个安全漏洞。
因此,如果应用程序的模块可以在很少通信的情况下独立运行,那么如果能够负担得起启动和关闭成本,则可能应该使用进程。IPC对性能的影响将是最小的,并且您在漏洞和安全漏洞方面会稍微安全一些。如果您需要获得或拥有大量共享数据(例如复杂的数据结构),那么请使用线程。