Linux 多线程服务端编程读书笔记(三)

    xiaoxiao2023-10-26  31

    Linux 多线程服务端编程笔记(三)

    第三章 多线程服务器的适用场合与常用编程模型

    1、进程与线程
    每个进程都有自己独立的地址空间线程的特点是共享地址空间,从而有效的共享数据。多个进程能有效的共用代码段(操作系统可以映射为同样的代码段),但不能共享数据多线程的价值是为了更好的发挥多核处理器效能
    2、单线程服务器的常用编程模型用
    模型: non-blocking IO + IO multiplexing(reactor 模式,事件驱动)——非阻塞IO+IO复程序的基本结构是一个事件循环,以事件驱动和事件回调的方式实现业务逻辑优点:编程简单,效率不错,不仅可以用于读写socket,连接的建立(connect(2)/accept())甚至DNS解析都可以进行,以提高并发度和吞吐量,对于IO密集的应用是一个不错的选择缺点:要求事件回调函数必须是非阻塞的,对于涉及网络IO的请求响应式协议,容易割裂业务逻辑,使其散步在多个回调函数之中,不易理解与维护
    3、 多线程服务器的常用编程模型

    每个请求创建一个线程,同样适用非阻塞IO操作

    线程数目固定,可以在程序启动的时候设置,不会频繁的创建与销毁可以方便地在线程间调配负载IO事件发生的线程时固定的,同一个TCP连接不必考虑事件的并发

    使用线程池,同样使用非阻塞IO操作

    使用非阻塞IO +IO复用

    Leader/Follower等高级模式

    推荐使用的模式:one loop per thread + thread pool , 即每个IO线程里面有一个事件循环(Reactor)用于处理读写或定时事件,线程池用来做计算,具体可以是任务队或生产者消费者队列

    4、进程间通信只用TCP
    linux 系统进程间通信方式:无名管道、有名管道、消息队列、共享内存、信号等同步原语有:互斥器、条件变量、读写锁、文件锁、信号量进程间通信首选socket 原因:跨主机,具有伸缩性,如果一台机子不够用,就将进程分散到同一局域网的多台机器上,改改主机端口号就能使用TCP port由一个进程独占,且操作系统会自动回收,即使程序意外退出,也不会给操作系统留下垃圾,当然以上的好处就是防止程序重复启动,后面那个进程抢不到port自然就没办法初始化了两个进程通过TCP通信,如果一个崩溃了,操作系统会关闭连接另一个进程几乎立刻就能感知到TCP还可以跨语言,服务器和客户端不必使用同一种语言为什么两个进程在同一台机器,就用共享内存是不必要的? 不值得为一点点性能提升是代码的复杂度增加,何况TCP的本地吞吐量不低TCP是字节流协议,只能顺序读取,有写缓冲,共享内存是消息协议,a进程填好一段内存来b进程来读,基本是stop wait方式。
    5、分布系统中使用TCP长连接通信

    TCP长连接与短连接?参考

    短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接(管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段

    连接——>数据传输——>关闭连接

    所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持(不发生RST包和四次挥手)。

    连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接(一个TCP连接通道多个读写通信);

    这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态

    两者的应用场景

    长连接多用于操作频繁(读写),点对点通信,而且连接数不能太大,如数据库的连接而像WEB网站的http服务一般都用短链接(http1.0只支持短连接,1.1keep alive 带时间,操作次数限制的长连接),因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好;

    TCP长连接的好处

    容易定位分布式系统中的服务之间的依赖关系,运行netstat -tpna | grep :port 就能列出到某服务端的客户端地址通过接受和发送队列的长度也比较容易定位网络或程序故障
    6、服务端网络编程处理并发连接的两种方式

    一台机器上可以创建远远高于CPU数目的线程,一个线程只处理一个TCP连接(甚至半个)。通常使用阻塞IO(有返回才继续进行)

    只能创建与CPU数目相当的线程,这是一个线程要处理多个TCP连接上的IO,通常使用非阻塞IO和IO复用

    本书只考虑后面一种模式

    7、必须使用单线程的场合
    程序可能会fork(2),只有单线程才会fork 一个线程fork之后有两种行为 立刻执行exec(),变身为另一个程序,如shell和inetd,看门狗进程slave,他会启动别的job进程因此需要单线程模型(守护进程)不调用exec(),继续当前程序。要么通过共享文件描述符与进程通信协同完成任务,要么接过父进程传来的fd独立完成任务 限制程序的CPU占用率单线程程序的优缺点 优点,简单缺点:事件循环有个明显的缺点,它是非抢占式的。比如时间a和事件b,事件a优先级要高于b,处理事件a要1ms,处理事件b要10ms。如果处理事件b高于事件a发生,事件a来临时,程序已经离开了poll调用。这样事件a要10ms后才会被处理。这样出现了优先级翻转,这个缺点可以用多线程来解决
    8、适用多线程的场景
    多个cpu可用线程间有共享可编辑的数据提供非均匀制服务,例如具有不同的优先级,我们可以用专门的线程来处理优先级高的事件,防止优先级翻转latency(延迟) 和 throughput(吞吐量) 同样重要,不是逻辑简单的 IO bound 或者 CPU bound 程序。即程序要有相当的计算量异步操作,如logging。无论是往磁盘中写logfile,还是往log server发送消息都不能阻塞绝对路径scale up,一个好的多线程程序应该享受增加CPU数目带来的好处具有可预测的性能,随着负载增加,性能缓慢下降,超过某个临界点之后会急剧下降,线程数目不随负载变化有效的划分责任与功能,让每个线程的逻辑简单,而不是所有的逻辑都塞到一个事件循环当中
    9、 线程的分类

    ​ 多线程服务程序中的线程大致分为3类

    IO线程,这类线程的主循环是IO复用,阻塞地等待在select/poll/epoll_wait系统调用上面

    计算线程,这类线程的主循环是blocking queue,阻塞等待条件变量上。一般位于线程池中。一般不涉及任何IO,一般要避免任何阻塞操作

    第三方库所在的线程,比如logging,database connection

    服务器程序一般不会频繁的启动和终止线程,只在启动时调用,在运行期间是不调用的

    10、多线程服务器场合 例释与答疑

    Linux能同时启动多少个线程

    对于32位系统,一个进程的地址空间是4GB,其中用户态能访问3G作用,一个线程的默认栈大小是10MB,故一个进程可以同时启动300个线程

    对于64位系统可以大大增加

    多线程能提高并发度么?

    如果是并发连接数则不能,由上一问可知,并发连接数上限是300远低于基于事件的单线程程序所能轻松达到的并发连接数(几千上万)

    one loop per thread 不逊于单线程程序,单个事件循环处理1万个连接并不罕见,一个多事件循环的多线程程序应该能轻松支持5万并发连接

    总结:thread per connection 不适合高并发场合。one loop per thread 的并发度足够大,与CPU数目成正比

    多线程编程能提高吞吐量么?

    对于计算密集型服务器,不能

    多线程能降低响应时间么?

    设计合理,充分利用多核资源,可以,在突发请求时尤为明显

    多线程程序如何让IO和计算相互重叠,降低时延

    把IO操作通过BlockingQueue交给别的线程去做,自己不必等待

    为啥第三方库往往需要自己的线程

    什么是线程池大小的阻抗匹配原则

    T = C/P;,线程池完全是计算P=1,线程池一半是计算,一半在等待IO,则P=0.5;,P<0.2时,不适用

    除了Reactor + thread pool 还有什么非阻塞编程思想

    有,Proctor.如果一次请求响应要和别的进程多次打交道,那么该模型能做到更高的并发度,代价是代码变得支离破碎。

    多线程的进程和单线程的多进程如何取舍?

    如果工作集较大,就要多线程,避免cpu cache换入换出,影响性能,否则就要单线程多进程

    最新回复(0)