转载:http://www.cnblogs.com/eavn/archive/2010/08/28/1811381.html
在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):
线程实验代码(thread.c):
两段程序做的事情是一样的,都是创建“若干”个进程/线程,每个创建出的进程/线程打印“若干”条“hello linux”字符串到控制台和日志文件,两个“若干”由两个宏 P_NUMBER和COUNT分别定义,程序编译指令如下:
diaoyf@ali:~/tmp1$ gcc -o fork fork.c
diaoyf@ali:~/tmp1$ gcc -lpthread -o thread thread.c
实验通过time指令执行两个程序,抄录time输出的挂钟时间(real时间):
time ./fork
time ./thread
每批次的实验通过改动宏 P_NUMBER和COUNT来调整进程/线程数量和打印次数,每批次测试五轮,得到的结果如下:
一、重复周丽论文实验步骤
第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 | |
---|---|---|---|---|---|---|
多进程 | 0m1.277s | 0m1.175s | 0m1.227s | 0m1.245s | 0m1.228s | 0m1.230s |
多线程 | 0m1.150s | 0m1.192s | 0m1.095s | 0m1.128s | 0m1.177s | 0m1.148s |
进程线程数:255 / 打印次数:100
第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 | |
---|---|---|---|---|---|---|
多进程 | 0m6.341s | 0m6.121s | 0m5.966s | 0m6.005s | 0m6.143s | 0m6.115s |
多线程 | 0m6.082s | 0m6.144s | 0m6.026s | 0m5.979s | 0m6.012s | 0m6.048s |
进程线程数:255 / 打印次数:500
第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 | |
---|---|---|---|---|---|---|
多进程 | 0m12.155s | 0m12.057s | 0m12.433s | 0m12.327s | 0m11.986s | 0m12.184s |
多线程 | 0m12.241s | 0m11.956s | 0m11.829s | 0m12.103s | 0m11.928s | 0m12.011s |
进程线程数:255 / 打印次数:1000
第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 | |
---|---|---|---|---|---|---|
多进程 | 1m2.182s | 1m2.635s | 1m2.683s | 1m2.751s | 1m2.694s | 1m2.589s |
多线程 | 1m2.622s | 1m2.384s | 1m2.442s | 1m2.458s | 1m3.263s | 1m2.614s |
进程线程数:255 / 打印次数:5000
本轮实验是为了和周丽论文作对比,因此将进程/线程数量限制在255个,论文也是测试了255个进程/线程分别进行10 次,50 次,100 次,200 次……900 次打印的用时,论文得出的结果是:任务量较大时,多进程比多线程效率高;而完成的任务量较小时,多线程比多进程要快,重复打印 600 次时,多进程与多线程所耗费的时间相同。
虽然我的实验直到5000打印次数时,多进程才开始领先,但考虑到使用的是NPTL线程库的缘故,从而可以证实了论文的观点。从我的实验数据看,多线程和多进程两组数据非常接近,考虑到数据的提取具有瞬间性,因此可以认为他们的速度是相同的。
当前的网络环境中,我们更看中高并发、高负荷下的性能,纵观前面的实验步骤,最长的实验周期不过1分钟多一点,因此下面的实验将向两个方向延伸,第一,增加并发数量,第二,增加每进程/线程的工作强度。