开发程序的方案阶段经常需要用到多线程或者多进程。本文介绍两种方式的优劣性:
一、多线程
优点:
- 无需跨进程边界
- 程序逻辑和控制方式简单
- 所有线程可以直接共享内存和变量等
- 线程方式消耗的总资源比进程方式好
缺点:
- 每个线程与主程序共用地址空间,受限于2GB地址空间
- 线程之间的同步和加锁控制比较麻烦
- 一个线程的崩溃可能影响到整个程序的稳定性
- 到达一定的线程数程度后,即使再增加CPU也无法提高性能
- 线程能够提高的总性能有限,而且线程数量较大时,线程本身的调度开销不小
提高多线程程序效率的一般方法:
- 不要频繁创建,销毁线程,
- 使用线程池减少线程间同步和通信(最为关键)
- 避免需要频繁共享写的数据
- 合理安排共享数据结构,避免伪共享(false sharing)
- 使用非阻塞数据结构/算法
- 避免可能产生可伸缩性问题的系统调用(比如mmap)
- 避免产生大量缺页异常,
- 尽量使用Huge Page可以的话使用用户态轻量级线程代替内核线程
二、多进程
优点:
- 顺序程序的特点:具有封闭性和可再现性;
- 程序的并发执行和资源共享。多道程序设计出现后,实现了程序的并发执行和资源共享,提高了系统的效率和系统的资源利用率。
缺点:
- 操作系统调度切换多个线程要比切换调度进程在速度上快的多。而且进程间内存无法共享,通讯也比较麻烦。
- 线程之间由于共享进程内存空间,所以交换数据非常方便;在创建或撤消进程时,由于系统都要为之分配和回收资源,导致创建进程撤销进程的系统的开销明显大于创建或撤消线程时的开销。
三、总结
多线程比多进程成本低,但性能更低。在UNIX环境,多进程调度开销比多线程调度开销,没有显著区别,就是说,UNIX进程调度效率是很高的。内存消耗方面,二者只差全局数据区,现在内存都很便宜,服务器内存动辄若干G,根本不是问题。多进程是立体交通系统,虽然造价高,上坡下坡多耗点油,但是不堵车。多线程是平面交通系统,造价低,但红绿灯太多,老堵车。
多线程的优点:无需跨进程边界; 程序逻辑和控制方式简单; 所有线程可以直接共享内存和变量等; 线程方式消耗的总资源比进程方式好; 多线程缺点:每个线程与主程序共用地址空间,受限于2GB地址空间; 线程之间的同步和加锁控制比较麻烦; 一个线程的崩溃可能影响到整个程序的稳定性; 到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数; 线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU多进程优点:每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系; 通过增加CPU,就可以容易扩充性能; 可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系; 每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大 多线程缺点:逻辑控制复杂,需要和主程序交互; 需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 多进程调度开销比较大; 最好是多进程和多线程结合,即根据实际的需要,每个CPU开启一个子进程,这个子进程开启多线程可以为若干同类型的数据进行处理。当然你也可以利用多线程+多CPU+轮询方式来解决问题……
日常写程序时,我们经常会听到一种说法:“使用多线程,可以增加系统吞吐率,或是可以增加系统扩展性。”的确,对于一个多线程的系统来说,在有合理的资源分配的情况下,可以增加系统中处理请求操作的资源实体,进而提升系统能够同时处理的请求数,即吞吐率。下面的左图是我们采用多线程时所期待的结果。
但是,请你注意,通常情况下,在我们采用多线程后,如果没有良好的系统设计,实际得到的结果,其实是右图所展示的那样。我们刚开始增加线程数时,系统吞吐率会增加,但是,再进一步增加线程时,系统吞吐率就增长迟缓了,有时甚至还会出现下降的情况。
为什么会出现这种情况呢?一个关键的瓶颈在于,系统中通常会存在被多线程同时访问的共享资源,比如一个共享的数据结构。当有多个线程要修改这个共享资源时,为了保证共享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,就会带来额外的开销。
另外,不同的操作系统下,多线程和多进程的表现也不尽相同。
WINDOWS操作系统针对线程单独优化,线程操作的效率优于Linux。
而Linux的”线程”和”进程”实际上处于一个调度层次,共享一个进程标识符空间。其创建进程的速度相比于Windows很快,但是线程的表现要相对欠缺。(众多的Linux线程库实现尝试都只能尽可能实现POSIX的绝大部分语义,并在功能上尽可能逼近。由于异步信号是内核以进程为单位分发的,而LinuxThreads的每个线程对内核来说都是一个进程,且没有实现”线程组”,因此,某些语义不符合POSIX标准,比如没有实现向进程中所有线程发送信号,README对此作了说明。LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。)
综上所述的结论是在Linux下编程多用多进程编程少用多线程编程。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。