读书笔记:多线程服务器的适用场合(1)

  声明:以下内容若无特别说明,均指Linux服务器环境下,传输层协议为TCP、主要开发语言为C++。

  开发服务器端程序最基础的工作就是处理并发连接,服务器端网络编程处理并发连接主要有以下两种方式:

  • 当线程廉价时,一台机器上可以创建远多于机器CPU物理线程数的“线程”,这是一个线程只处理一个TCP连接,通常使用阻塞IO(至少看起来如此)。例如Go goroutine、Erlang actor。这里的线程由语言runtime调用,与操作系统的线程不是一回事。
  • 当线程很宝贵时,一台机器上只能创建与机器CPU数目相当的线程。这是,一个线程处理多个 TCP连接上的IO,通常使用非阻塞IO和IO多路复用。例如libevent、boost.asio。这是原生线程,能被操作系统的任务调度器看见。

  对于C++服务器端开发,只考虑后一种方式。

  首先,一个有多台机器组成的分布式系统必然是多进程的,因为进程不能跨操作系统边界。在这个前提下,我们把目标集中到一台机器,一台至少拥有4核心的普通服务器。如果要在一台多核服务器上提供一个服务,可用的模式有:

  1. 运行一个单线程的进程:模式1是不可伸缩的(scalable),不能发挥多核机器的计算能力。
  2. 运行一个多线程的进程:模式2难写。
  3. 运行多个单线程的进程:a)简单地把模式1的进程运行多份,b)主进程+worker进程,如果必须要绑定到一个TCP port。
  4. 运行多个多线程的进程:模式4不但没有结合2和3的优点,反而结合了2和3的缺点。

重点讨论2和3b)的优劣,即:什么时候一个服务器程序应该是多线程(模式2)的?从功能上讲,没有什么是多线程能做到而单线程做不到的,反之亦然。从性能上讲,无论是IO bound还是CPU bound的服务,多线程都没有什么优势。如以下情况

1.单线程的适用场景

  一般有两种场景必须适用单线程;

  • 程序可能会fork():如集群中在计算节点上负责启动任务的的的守护进程(即“看门狗”程序)。
  • 限制CPU占用率:例如一个8核的服务器上,一个单线程的程序即便发生busy-wait,占满一个核心,其CPU使用率也只有12.5%。最坏情况下仍有87.5%的计算资源可用。

从编程的角度上说,单线程优势就是:简单。程序的结构一般是一个IO多路复用的event loop,直接使用阻塞IO。Event loop有一个明显的缺点:它是非抢占的,耗时短且优先级高的的task可能需要等到的耗时长且优先级低的task完成后才能进行,相当于发生了优先级反转。前面我说,无论是IO bound还是CPU bound,多线程都没有什么绝对意义上的优势。这句话的意思是,如果用很少的CPU负载就能让IO跑满,或者用很少的IO流量就能让CPU跑满,那么多线程没啥用。前者例子很多,如静态web服务器,或者FTP服务器,CPU的负载很轻,主要瓶颈在网络IO和磁盘IO。这个时候一个单线程程序就能撑满IO。增加CPU的数目也不能提升吞吐量。CPU跑满的情况较少见,如subset sun问题,对于这种情况3a)最适合,发挥多核优势。

  也就是说,如果一方早早地先到达的瓶颈,多线程程序都没什么优势。

2.多线程的适用场景

  多线程的适用场景是:提高响应速度,让IO和计算相互重叠,降低延时。虽然多线程不能提高绝对性能,但是可以提高平均响应性能。一个程序要做成多线程程序,大致要满足:

  • 有多个CPU可用。单核上多线程没有性能优势(但是或许可简化并发业务逻辑的实现)。
  • 多线程可有效地划分责任和功能模块,让每一个线程的逻辑简单,任务单一,便于编码。而不是把所有逻辑塞到一个event loop中,不同类别的事件之间相互影响。
  • 线程中有共享数据,即内存的全局状态。但是应该尽量减少线程间的共享数据。
  • 共享的数据可以修改,不是静态的。如果数据是静态的,利用进程间shared memory,模式3即可。
  • 事件的响应有优先级。可以用专门的线程来处理优先级高的事件。
  • 时延和吞吐量同样重要,不是简单的IO bound或CPU bound程序。
  • 利用异步操作。无论往磁盘写log file,还是往log server发消息都不该阻塞关键路径。
  • 具有可预测的性能。随着负载增加,性能缓慢下降,超过某个临界点后计算下降。线程数目不随负载变化。

例子暂略。

线程的分类

  一个多线程服务程序中大致可以分为3类:

  1. IO线程。这类线程的主循环是IO多路复用,阻塞地等在select/poll/epoll_wait系统调用上。这类线程也处理定时时间。当然也不全是IO处理,一些简单的计算也可以放入其中,如消息的编码解码。
  2. 计算线程,这类线程的主循环是阻塞队列,阻塞地等在条件变量上。这类线程一般在线程池中。这种线程通常不涉及IO,一般要避免任何阻塞操作。
  3. 第三方库所用的线程,比如logging,又比如database connection。

  服务器端程序一般不会频繁启动或终止线程。

时间: 2024-10-24 16:45:56

读书笔记:多线程服务器的适用场合(1)的相关文章

多线程服务器的适用场合

原文:http://blog.csdn.net/Solstice/article/details/5334243 陈硕 (giantchen_AT_gmail) Blog.csdn.net/Solstice 2010 Feb 28 这篇文章原本是前一篇博客<多线程服务器的常用编程模型>(以下简称<常用模型>)计划中的一节,今天终于写完了. “服务器开发”包罗万象,本文所指的“服务器开发”的含义请见<常用模型>一文,一句话形容是:跑在多核机器上的 Linux 用户态的没有

[读书笔记]多线程基础

转自:http://www.cnblogs.com/huangxincheng/category/362940.html 第一天 尝试Thread 一:Thread的使用 我们知道这个类代表处理器线程,在Thread中有几个比较常用和重要的方法. <1> sleep:  这个算是最简单的了. <2> join:    这个可以让并发行处理变成串行化,什么意思呢?上代码说话最清楚. <3> Interrupt和Abort:这两个关键字都是用来强制终止线程,不过两者还是有区

《Linux多线程服务器端编程》读书笔记第3章

<Linux多线程服务器端编程>第3章主要讲的是多线程服务器的适用场合与常用的编程模型. 1.进程和线程 一个进程是"内存中正在运行的程序“.每个进程都有自己独立的地址空间(address space).将"进程"比喻为"人",每个人都有自己的记忆(memory),人与人通过谈话(消息传递)来交流,谈话既可以是面谈(同一台服务器),也可以在电话里谈(不同的服务器,有网络通信).面谈和电话谈的区别在于,面谈可以立即知道对方是否死了(crash,S

《Java编程那点事儿》读书笔记(七)——多线程

1.继承Thread类 通过编写新的类继承Thread类可以实现多线程,其中线程的代码必须书写在run方法内部或者在run方法内部进行调用. public class NewThread extends Thread { private int ThreadNum; public NewThread(int ThreadNum){ this.ThreadNum = ThreadNum; } public void run(){ try{ for(int i = 0;i < 10;i ++){ T

《Linux多线程编程手册》读书笔记

第二章 基本线程编程 1.(P25)如果多个线程等待同一个线程终止,则所有等待线程将一直等到目标线程终止.然后,一个等待线程成功返回,其余的等待线程将失败并返回ESRCH错误. 2.(P26)将新线程的pbe参数作为栈参数进行传递.这个线程参数之所以能够作为栈参数传递,是因为主线程会等待辅助线程终止.不过,首选方法是使用malloc从堆分配存储,而不是传递指向线程栈存储的地址.如果将该参数作为地址传递到线程栈存储,则该地址可能无效或者在线程终止时会被重新分配. 3.(P28)pthread_de

《高性能MySQL》读书笔记--优化服务器设置

MySQL有大量可以修改的参数--但不应该随便去修改.通常只需要把基本的项配置正确(大部分情况下只有很少一些参数是真正重要的),应该将更多的时间花在schema的优化.索引,以及查询设计上.在正确地配置了MySQL的基本配置项之后,再花力气去修改其它配置项的收益通常就比较小了. 1.创建MySQL配置文件 建议不要使用操作系统的安装包自带的配置文件,最好从头开始创建一个配置文件.(首先要确定MySQL使用了哪个配置文件!) 2.InnoDB缓冲池(Buffer Pool) 有一个流行的经验法则说

高可用读书笔记1.1--了解服务器

1.查看硬盘及分区信息 fdisk -l 2.查看文件系统的磁盘看空间占用情况 df  -h 3.查看磁盘的io性能 iostat -x 1 10 iostat是含在套装systat中的,在centos下面可以用命令yum -y install sysstat安装 4.查看系统中某个目录的大小 du -sh 目录名称 5.查看分区中占用最多空间的前10个文件或目录 进入分区挂载点 du -cksh * |sort -rn|head -n 10 6.查看平均负载的命令 top,uptime 不过我

JAVA编程思想读书笔记(五)--多线程

接上篇JAVA编程思想读书笔记(四)--对象的克隆 No1: daemon Thread(守护线程) 参考http://blog.csdn.net/pony_maggie/article/details/42441895 daemon是相于user线程而言的,可以理解为一种运行在后台的服务线程,比如时钟处理线程.idle线程.垃圾回收线程等都是daemon线程. daemon线程有个特点就是"比较次要",程序中如果所有的user线程都结束了,那这个程序本身就结束了,不管daemon是否

【转】《windows核心编程》读书笔记

这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁,往往是很多单独的字句,后面的内容更为连贯. 海量细节. 第1章    错误处理 1.         GetLastError返回的是最后的错误码,即更早的错误码可能被覆盖. 2.         GetLastError可能用于描述成功的原因(CreatEvent)