并发模型

常见的并发模型一般包括3类,基于线程与锁的内存共享模型,actor模型和CSP模型,其中尤以线程与锁的共享内存模型最为常见。由于go语言的兴起,CSP模型也越来越受关注。基于锁的共享内存模型与后两者的主要区别在于,到底是通过共享内存来通信,还是通过通信来实现访问共享内存。由于actor模型和CSP模型,本人并不是特别了解,我主要说说最基本的并发模型,基于线程与锁的内存共享模型。

为什么要并发,本质都是为了充分利用多核CPU资源,提高性能。但并发又不能乱,为了保证正确性,需要通过共享内存来协调并发,确保程序正确运转。无论是多进程并发,还是多线程并发,要么通过线程间互斥同步(spinlock,rwlock,mutex,condition,信号量),要么通过进程间通信(共享内存,管道,信号量,套接字),本质都是为了协同。多线程和多进程本质类似,尤其是linux环境下的pthread库,本质是用轻量级进程实现线程。下面以网络服务为例,简单讨论下多线程模型的演进。

最简单的模型是单进程单线程模型,来一个请求处理一个请求,这样效率很低,也无法充分利用系统资源。那么可以简单的引入多线程,其中抽出一个线程监听,每来一个请求就创建一个工作线程服务,多个请求多个线程,这就是多线程并发模型。这种模式下,资源利用率是上去了,但是却有很多浪费,线程数与请求数成正比,意味着频繁的创建/销毁线程开销,频繁的上下文切换开销,这些都是通过系统调用完成,需要应用态到内核态的切换,导致sys-cpu偏高,资源并没有充分利用在处理请求上。

为了缓解这个问题,引入线程池模型,简单来说,就是预先创建好一批线程,并且加大线程的复用能力,将线程数控制在一定数目内,缓解上下文切换开销。以MySQL线程池为例,原来多线程模型是单连接单线程,现在变成单语句单线程,提高了线程复用效率。如果线程在执行过程中遇到等待(锁等待,IO等待),那么线程挂起,并减少活跃线程数,告知线程池系统活跃线程可能不够,需要追加线程,然后等系统空闲时,再减少线程数目,做到根据系统负载平衡线程数目。为了做到极致,更进一步减少上下文切换开销,引入了协程,协程只是一种用户态的轻量线程,它运行在用户空间,不受系统调度。它有自己的调度算法。在上下文切换的时候,协程在用户空间切换,而不是陷入内核做线程的切换,减少了开销。协程的并发,是单线程内控制权的轮转,相比抢占式调度,协程是主动让权,实现协作。协程的优势在于,相比回调的方式,写的异步代码可读性更强。缺点在于,因为是用户级线程,利用不了多核机器的并发执行。简单总结下:

单线程-->(单线程轮询处理,太慢)
多线程-->(多线程会频繁地创建、销毁线程,这对系统也是个不小的开销。这个问题可以用线程池来解决。)
线程池-->(仍然有多线程上下文切换的问题,调度由内核调度)
协程-->(应用层调度,不touch内核)

I/O模型
    linux中所有物理设备对于系统而言都可以抽象成文件,包括网卡,对应的就是套接字,磁盘对应的文件,以及管道等。因此所有对物理设备的读写操作都可以抽象为IO操作,典型的IO操作模型分为以下几类,阻塞IO,非阻塞IO,I/O多路复用,异步非阻塞IO以及异步IO等。

IO模型分类
阻塞I/O--> 原生的read/write系统调用,默认导致线程阻塞;
非阻塞I/O -->通过指定系统调用read/write的参数为非阻塞,告知内核fd没就绪时,不阻塞线程,而是返回一个错误码,应用死循环轮询,直到fd就绪;
I/O多路复用-->(select/poll/epoll),对通知事件堵塞,对于I/O调用不堵塞。
异步I/O(异步非阻塞)-->告知内核某个操作(读写I/O),并让内核在整个操作(包括将数据复制到我们的进程缓冲区)完成后通知。

I/O多路复用
    常见的I/O多路复用主要用于网络IO场景,主要有select,poll和epoll机制。对比同步I/O,实际上是对I/O请求加了一层代理,由这些代理去监听通知事件(是否网络包到来),然后再通知用户去读写数据。这种方式也是一种阻塞I/O,代理对通知事件阻塞,这里的代理一般指监听线程。对比select,poll提升了最大支持文件描述符数目,从1024提升到65535,MySQL中的半同步复制还因为使用select的这个限制,导致半同步中断的bug(链接)。

对比select和poll机制,epoll通过事件表管理用户感兴趣的事件,无需反复传入用户感兴趣事件,处理事件通知的时间复杂度是O(1),而select,poll机制的时间复杂度是O(N)。另外select/poll只能工作在LT模式(水平触发模式);而epoll不仅支持LT模式,还支持ET模式(边缘触发模式)。两种模式的主要区别是,有数据可读时,LT模式会不停的通知,直到数据被获取,这种模式不用担心通知事件丢失;ET模式只会通知一次,因此对比LT少很多epoll系统调用,效率更高。epoll对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。从本质上讲,与LT相比,ET模型是通过减少系统调用来达到提高并行效率的。

libev/libeasy
      epoll很好用,但是要使用epoll,fd,signal,timer分别要采用不同的机制才能一起工作。libev第一个要做的事情就是把系统资源统一成一种调用方式。因为都需要在读写事件就绪后自己负责进行读写,也就是读写过程是阻塞的。libev的核心是事件处理框架,最常见的是就是一个所谓的Reactor事件处理框架和设计模式。Reactor对象负责实现主循环(其中有事件分离器的调用),定义事件处理接口,用户程序向Reactor注册事件回调的实现类(从接口继承),Reactor主循环在收到事件的时候调用相应的回调函数。libeasy实现类似libev和libevent的功能,包括HTTP服务器等,不同的是,它基于libev做了包装,提供了同一个的资源fd和loop机制,线程池,异步框架等实现。

AIO
说到AIO,一般是说磁盘的异步I/O,linux早期的版本并没有真正的AIO接口,所谓的AIO其实是多线程模拟的,在应用态完成。具体而言就是有一个队列存储IO请求,通过一组工作线程提取任务,并发起同步IO,待IO完成后,再通知用户已经完成了。对于用户而言,由于是提交IO请求后就直接返回,然后再被通知IO已经完成,所以可以认为是异步I/O,这种异步I/O实现机制主要指POXIS AIO,MySQL的InnoDB引擎也实现了一套类似的AIO机制。后面linux内核引入了真正的AIO,主要区别在于发起I/O调用不再是同步调用,IO请求统一在内核层面排队,并且一次可以提交一批异步IO请求,然后通过轮询或者回调的方式接收完成通知即可。相比于POXIS AIO,底层有更多的IO并行,IO和CPU能充分并发,大大提升性能。在使用中,通过-lrt链接使用AIO库是POXIS接口,而通过-laio链接使用的AIO库是linux Native AIO接口。常用接口包括 io_setup,io_destroy,io_submit,io_cacel和io_getevents等。

同步IO:
优点:简单
缺点:IO阻塞,无法充分利用IO和CPU资源,效率低

Native AIO:
优点:AIO可以支持一次发送多个不连续的异步IO请求,性能更好(同步IO需要发送多次)
缺陷:需要文件系统支持O_DIRECT选项,如果不支持,io_submit实际上是“退化”成同步操作。

POSIX AIO:
优点:不依赖O_DIRECT选项,有一定的合并能力(相邻地址的请求,可以做merge)。
缺点:并发的IO请求受限于线程数目;另外就是,可能慢速磁盘,可能导致其它新的请求没有及时处理(工作线程数不够了)。

参考文档
https://cloud.tencent.com/developer/article/1349213
https://my.oschina.net/dclink/blog/287198
https://www.cnblogs.com/lojunren/p/3856290.html
https://www.ibm.com/developerworks/cn/linux/l-async/

并发模型与IO模型梳理的更多相关文章

  1. python并发编程之IO模型 同步 异步 阻塞 非阻塞

    IO浅谈 首先 我们在谈及IO模型的时候,就必须要引入一个“操作系统”级别的调度者-系统内核(kernel),而阻塞非阻塞是跟进程/线程严密相关的,而进程/线程又是依赖于操作系统存在的,所以自然不能脱 ...

  2. Python并发编程之IO模型

    目录 IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) IO多路复用 异步IO IO模型比较分析 selectors模块 一.IO模型介绍 Stevens ...

  3. python并发编程之IO模型,

    了解新知识之前需要知道的一些知识 同步(synchronous):一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行 #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调 ...

  4. 【并发编程】IO模型

    一.要点回顾 为了更好地了解IO模型,我们需要先回顾下几个概念:同步.异步.阻塞.非阻塞 同步: 一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行.就是在发出一个功能调用时,在没 ...

  5. 并发编程之IO模型

    一.阻塞IO(blocking IO) from concurrent.futures import ThreadPoolExecutor import socket server = socket. ...

  6. Python之旅:并发编程之IO模型

    一 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非 ...

  7. 并发编程(三) IO模型

    五 IO模型 常用的IO模型有4种: 阻塞IO 非阻塞IO IO多路复用 异步IO 不常用的有: 驱动信号 5.1 阻塞IO.非阻塞IO 阻塞IO:进程不能做其他的事情 非阻塞IO:等待数据无阻塞 阻 ...

  8. python全栈开发从入门到放弃之socket并发编程之IO模型

    一 IO模型介绍 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问 ...

  9. 并发编程之IO模型比较和Selectors模块

    主要内容: 一.IO模型比较分析 二.selectors模块 1️⃣ IO模型比较分析 1.前情回顾: 上一小节中,我们已经分别介绍过了IO模型的四个模块,那么我想大多数都会和我一样好奇, 阻塞IO和 ...

随机推荐

  1. 使用cmd打开磁盘目录和文件

    例如: 1.进入e盘 2.查看E盘下所有文件 3.进入指定文件夹

  2. leetcode的Hot100系列--3. 无重复字符的最长子串--滑动窗口

    可以先想下这两个问题: 1.怎样使用滑动窗口? 2.如何快速的解决字符查重问题? 滑动窗口 可以想象一下有两个指针,一个叫begin,一个叫now 这两个指针就指定了当前正在比较无重复的字符串,当再往 ...

  3. android_activity_研究(一)

    android中活动的概念(activity)是一个很重要的东东.这里有很多东东值得好好研究.最好的研究来源当然是官网啦,所以本人这里写一点对官网文章的研究心得. 一.活动(activity)的概念 ...

  4. Java编程思想:文件读写实用工具

    import java.io.*; import java.util.ArrayList; import java.util.Arrays; public class Test { public st ...

  5. a=re.findall('b',c)报错提示:TypeError:expected string or buffer

    目的:想通过findall选取某个unicode编码的字符串列表(列表里面有元组) 问题:报错[TypeError:expected string or buffer] 现在测试下: 定义一个有元组的 ...

  6. Hyperledger Fabric 之 Channel ,创建channel链接几项注意点

    好长时间没有更新博客,网上也有很多fabric的部署资料,而且也都很不错,也比较全面.我就再想重复的工作暂时就不用做了,后面抽时间在做细化和分类:就将学习和工作中遇到和解决的问题经验,做一些分享. 而 ...

  7. MetInfo企业网站管理系统 5.3 全新安装

    在phpStudy\PHPTutorial\WWW的文件下创建MetInfo文件夹.把MetInfo5.3解压到MetInfo文件夹里 用浏览器访问127.0.0.1/MetInfo 同意安装 下一步 ...

  8. 使用FastReport.net 报表在网页上实现打印功能

    这些年的工作当中,最早是在8年前接触到FastReport这个报表工具,从名字上来看,直译过来就是快速报表,正所谓天下武功,唯快不破,FastReport报表早些年确实是制作报表的不二之选,8年前的工 ...

  9. linux初学者-pxe装机篇

    linux初学者-pxe装机篇 PXE的网络装机是客户机从自己的网卡启动,向本网络中的DHCP服务器索取ip,并从本网络的TFTP服务器中索取启动文件进行装机.此装机需要kickstart.tftp. ...

  10. SpringMVC简易架构图。。。

    DispatcherServlet拦截所有请求 -> 通过访问url路径找到对应的控制器 -> 通过适配器调用控制器的方法 -> 控制器里面的方法处理业务 -> 通过视图解析器 ...