在Unix上编程采用多线程还是多进程的争执由来已久,这种争执最常见到在C/S通讯中服务端并发技术 的选型上,比如WEB服务器技术中,Apache是采用多进程的(perfork模式,每客户连接对应一个进程,每进程中只存在唯一一个执行线程), Java的Web容器Tomcat、Websphere等都是多线程的(每客户连接对应一个线程,所有线程都在一个进程中)。
从Unix发展历史看,伴随着Unix的诞生进程就出现了,而线程很晚才被系统支持,例如Linux直到内核2.6,才支持符合Posix规范的NPTL线程库。进程和线程的特点,也就是各自的优缺点如下:
进程优点:编程、调试简单,可靠性较高。
进程缺点:创建、销毁、切换速度慢,内存、资源占用大。
线程优点:创建、销毁、切换速度快,内存、资源占用小。
线程缺点:编程、调试复杂,可靠性较差。
上面的对比可以归结为一句话:“线程快而进程可靠性高”。线程有个别名叫“轻量级进程”,在有的书籍资料上介绍线程可以十倍、百倍的效率快于进程; 而进程之间不共享数据,没有锁问题,结构简单,一个进程崩溃不像线程那样影响全局,因此比较可靠。我相信这个观点可以被大部分人所接受,因为和我们所接受 的知识概念是相符的。
在写这篇文章前,我也属于这“大部分人”,这两年在用C语言编写的几个C/S通讯程序中,因时间紧总是采用多进程并发技术,而且是比较简单的现场为 每客户fork()一个进程,当时总是担心并发量增大时负荷能否承受,盘算着等时间充裕了将它改为多线程形式,或者改为预先创建进程的形式,直到最近在网 上看到了一篇论文《Linux系统下多线程与多进程性能分析》作者“周丽 焦程波 兰巨龙”,才认真思考这个问题,我自己也做了实验,结论和论文作者的相似,但对大部分人可以说是颠覆性的。
下面是得出结论的实验步骤和过程,结论究竟是怎样的? 感兴趣就一起看看吧。
实验代码使用周丽论文中的代码样例,我做了少量修改,值得注意的是这样的区别:
论文实验和我的实验时间不同,论文所处的年代linux内核是2.4,我的实验linux内核是2.6,2.6使用的线程库是NPTL,2.4使用的是老的Linux线程库(用进程模拟线程的那个LinuxThread)。
论文实验和我用的机器不同,论文描述了使用的环境:单 cpu 机器基本配置为:celeron 2.0 GZ, 256M, Linux 9.2,内核 2.4.8。我的环境是我的工作本本:单cpu单核celeron(R) M 1.5 GZ,1.5G内存,ubuntu10.04 desktop,内核2.6.32。
进程实验代码(fork.c):
-
#include <stdlib.h>
-
#include <stdio.h>
-
#include <signal.h>
-
-
#define P_NUMBER 255 /* 并发进程数量 */
-
#define COUNT 100 /* 每进程打印字符串次数 */
-
#define TEST_LOGFILE "logFile.log"
-
FILE *logFile = NULL;
-
-
char *s = "hello linux\0";
-
-
int main ( )
-
{
-
int i = 0,j = 0;
-
logFile = fopen (TEST_LOGFILE, "a+" ); /* 打开日志文件 */
-
for (i = 0; i < P_NUMBER; i++ )
-
{
-
if (fork ( ) == 0 ) /* 创建子进程,if(fork() == 0){}这段代码是子进程运行区间 */
-
{
-
for (j = 0;j < COUNT; j++ )
-
{
-
fprintf (logFile, "[%d]%s\n", j, s ); /* 向日志文件输出 */
-
}
-
exit ( 0 ); /* 子进程结束 */
-
}
-
}
-
-
for (i = 0; i < P_NUMBER; i++ ) /* 回收子进程 */
-
{
-
wait ( 0 );
-
}
-
-
return 0;
-
}
进程实验代码(thread.c):
-
#include <pthread.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
#include <stdio.h>
-
-
#define P_NUMBER 255 /* 并发线程数量 */
-
#define COUNT 100 /* 每线程打印字符串次数 */
-
#define Test_Log "logFIle.log"
-
FILE *logFile = NULL;
-
-
char *s = "hello linux\0";
-
-
print_hello_linux ( ) /* 线程执行的函数 */
-
{
-
int i = 0;
-
for (i = 0; i < COUNT; i++ )
-
{
-
fprintf (logFile, "[%d]%s\n", i, s ); /* 向日志文件输出 */
-
}
-
pthread_exit ( 0 ); /* 线程结束 */
-
}
-
-
int main ( )
-
{
-
int i = 0;
-
pthread_t pid [P_NUMBER ]; /* 线程数组 */
-
logFile = fopen (Test_Log, "a+" ); /* 打开日志文件 */
-
-
for (i = 0; i < P_NUMBER; i++ )
-
pthread_create (&pid [i ], NULL, ( void * )print_hello_linux, NULL ); /* 创建线程 */
-
-
for (i = 0; i < P_NUMBER; i++ )
-
pthread_join (pid [i ], NULL ); /* 回收线程 */
-
-