有书共读 《Linux多线程服务端编程》第3章

3.1 进程与线程
    进程:有独立的地址空间。可以共享代码段。
    服务端需考虑的点:
    1、容错:某个进程崩
    2、扩容:新进程加入
    3、负载均衡
    4、退休:某个进程修复bug,暂时不分配任务
    线程:共享地址空间。可以更好地发挥多核处理器的效能。

3.2 单线程服务器的常用编程模型
    主流:非阻塞IO+IO复用(即reactor模式)lighttpd(单线程服务器), libevent,libev, ACE, Java NIO, POE,Twisted
    在“非阻塞IO+IO复用”模型,程序的基本结构是一个事件循环,以事件驱动和事件回调的方式实现业务逻辑。
    reactor模型优点:编程简单,效率较高;
    可以读写socket,建立连接或DNS解析可以用非阻塞方式进行,以提高并发度和吞吐量;
    适用于IO密集的应用。
    基于事件驱动的编程模型的缺点:要求回调函数必须是非阻塞的。

3.3 多线程服务器的常用编程模型
3.3.1 one loop per thread
    程序里的每个IO线程有一个event loop(或者叫Reactor), 用于处理读写和定时事件。
    这种方式的好处:
    1. 线程数目基本固定,可以在程序启动的时候设置,不会频繁创建或销毁;
    2. 可以方便地在线程间调配负载;
    3. IO事件发生的线程是固定的,同一个TCP连接不用考虑事件并发。
    Eventloop代表线程的主循环,需要让哪个线程干活,就把timer或IO channel注册到哪个loop即可。

3.3.2 线程池
    对于只有计算任务没有io的线程,用blocking queue实现任务队列,如下:


3.3.3 推荐模式
    C++多线程服务端编程模型:one loop per thread(一个事件一个线程赖处理)+thread pool
    event loop:用作IO复用,配合非阻塞io和定时器。
    thread pool: 用来做计算,具体可以是任务队列或者生产者消费者队列。

3.4 进程间通信只用TCP
    Linux IPC:管道、消息队列、信号、共享内存、信号量、socket
    同步原语:互斥量、条件变量、读写锁、文件锁、信号量
    首选TCP socket,好处在于:
    1. 可以跨主机
    2. 具有伸缩性
    3. 进程意外退出时,操作系统会自动回收资源
    4. port独占,可以防止程序重复启动
    5. 可记录,可重现
    6. 可以跨语言
    tcp/socket 与共享内存相结合的缺点:
    代码复杂度上升,需要建一个抽象层,封装两种ipc,会带来不透明性和测试复杂度
    
    分布式系统中使用TCP长连接通信
    好处:1、容易定位分布式系统中的服务之间的依赖关系。netstat -tpa|grep :port就能列出服务的客户端地址,然后在客户端机器用netstat或lsof找出时哪个进程发起的连接。
                2、通过接受和发送队列的长度容易定位网络或程序故障。
3.5 多线程服务器的适用场合
    服务端开发的一个基本任务是处理并发连接,两种方式:
        1、线程很廉价时,一台机器可以创建远高于cpu数目的线程,一个线程处理一个tcp连接,使用阻塞io。
        2、线程宝贵时,一台机器只创建cpu数目相当的线程,一个线程处理多个连接,使用非阻塞和io复用,如libevent、muduo。
    在一台多核机器上提供一种服务或执行一个任务,可用的模式:
        1. 运行一个单线程的进程(不可伸缩,不能发挥多核计算机的计算能力)
        2.运行一个多线程的进程
        3.运行多个单线程的进程(模式1运行多份,主进程+工作进程)
        4.运行多个多线程的进程

3.5.1 必须用单线程的场合
    1. 程序可能会fork
    2. 限制程序的cpu占用率
    只有单线程程序能fork,多线程调用fork是灾难性的。见https://www.cnblogs.com/liyuan989/p/4279210.html
    fork之后一般有两种行为:
        1、立刻执行exec(),变身成另一个程序。(守护进程或叫看门狗进程)
        2、不调用exec(),继续运行当前程序。
    单线程程序能限制程序的CPU占用率。为了避免辅助性程序占用过多的cpu,可以做成单线程。

3.5.2 单线程程序的优缺点
    对于负载较轻、主要瓶颈在网络io或磁盘io的,一个单线程就可以。
    适合多线程的场景:
    提高响应速度,让io和计算相互重叠,降低延时,可以提高平均响应性能。
    一个线程做成多线程的情况:
        1. 有多个cpu可用。
        2. 线程间共享数据,即内存中的全局状态。
        3.共享的数据是可以修改的,而不是常量表。
        4. 提供非均质的服务,用专门线程来处理高优先级线程。
        5.利用异步操作,可扩展。
        6.具有可预测的性能。
        7.多线程能有效划分责任与功能,让每个线程的逻辑比较简单单一。
    线程的分类
        1. IO线程:主循环是IO复用,阻塞地等在select/poll/epoll系统调用上。
        2. 计算线程:主循环是阻塞队列。
        3.第三方库所用的线程

3.6 “多线程服务器的适用场合”例释与答疑
    1.Linux能同时开启多少个线程?
    3GB用户空间,一个线程默认栈大小为10MB,因此是300左右
    2. 多线程能提高并发度吗?
    thread per connection 不适合高并发场合,其可扩展性不好。one loop per thread的并发足够,且与cpu数目成正比。
    3. 多线程能提高吞吐量吗?
    对于计算密集型,不能。线程池不是万能的,其大小应该满足“阻抗匹配原则”。
    4. 多线程能降低响应时间吗?
    设计合理,充分利用多核资源的话可以。对于计算难度相差较大的任务,多线程可以降低简单任务被复杂任务压住的出现概率。
    5. 多线程程序如何让io和计算相互重叠,降低时延?
    把io操作通过阻塞队列交给别的线程去做,自己不必等待。
    6.什么是线程池大小的阻抗匹配原则?
    密集计算所占的时间比重为p(0,1),系统有c个cpu,线程池的大小的经验公式t=c/p。
    p<0.2时,这个公式就不适用了,t可以取一个固定值。
    7. 除了reactor+thread pool,还有别的多线程编程模型吗?
     有,preactor。如果一次响应要和别的进程打多次交道,preactor往往能做到更高的并发度。





#Linux##读书笔记##笔记#
全部评论

相关推荐

点赞 评论 收藏
转发
投递腾讯等公司8个岗位
点赞 评论 收藏
转发
点赞 10 评论
分享
牛客网
牛客企业服务