同步、异步、阻塞、非阻塞

同步:

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。

例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事

异步:

异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

例如 ajax请求(异步): 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕

阻塞

阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。

有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。 例如,我们在socket中调用recv函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。

非阻塞

非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

对象的阻塞模式和阻塞函数调用

对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状 态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。

  1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。
  2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)
  3. 阻塞, 就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
  4. 非阻塞, 就是调用我(函数),我(函数)立即返回,通过select通知调用者

    同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞!

    阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!

同步与异步:描述的是用户线程与内核的交互方式,同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍然继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

阻塞和非阻塞:描述的是用户线程调用内核IO操作的方式,阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

同步阻塞IO

应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。默认情况下所有的Socket是阻塞。

用户线程通过系统调用read发起IO操作,由用户空间转到内核空间,内核等待数据到达后,然后将数据从内核拷贝到用户线程。即用户需要等待read将socket中的数据读取到buffer后,才继续处理接收的数据。整个IO请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够。

在python网络编程中,socket的accept()和recv()都是阻塞的。

同步非阻塞IO

同步非阻塞IO是在同步阻塞IO的基础上,将socket设置为NONBLOCK,这样做用户线程可以在发起IO请求后可以立即返回。设置为非阻塞后,用户线程发起IO请求时立即返回。如果没读取到数据,用户线程需要不断地发起IO请求,直到数据到达后才真正读取到数据继续执行。

即用户需要不断地调用read,尝试读取socket中的数据,直到读取成功后,才继续处理接收的数据。整个IO请求的过程中,虽然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。而且在数据拷贝阶段没做任何改变,这个阶段依然是阻塞的。

IO多路复用

IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题。

户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。

从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

虽然此方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。

select、poll、epoll简析

select特点

1、 单个进程可监视的fd数量被限制,即能监听端口的大小有限。最大链接数限额(1024)

2、 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:

3、需要维护一个用来存放大量fd的数据结构,遍历所有的文件描述符(fd)查看是否有数据访问,这样会使得用户空间和内核空间在传递该结构时复制开销大

poll

poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

epoll

1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);

2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;

即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。

3、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

python示例代码(select)

#server端
import socket
import select
sock = socket.socket()
sock.bind(("127.0.0.1",9000))
sock.listen(5)
li = [sock]
while 1:
r = select.select(li,[],[]) #三个列表参数,输入列表(要监听的列表),输出列表,错误列表
for i in r[0]: #r[0]就是监听到变化的列表(fd变化)
if i == sock: #i == sock时说明新的客户端连接
conn,addr = i.accept()
li.append(conn) #将conn对象追加到监听列表
else: #i != sock时说明是客户端有数据发送
data = i.recv(1024)
print(data.decode('utf-8'))
response = input(">>>").strip() #input阻塞
i.send(response.encode('utf-8')) #client端
import socket
sock = socket.socket()
sock.connect(("127.0.0.1",9000))
while 1:
data = input('>>>').strip()
sock.send(data.encode('utf-8'))
response = sock.recv(1024)
print(response.decode('utf-8'))

高效IO多路复用

#高效IO多路复用(selectors)
import selectors
import socket
sel = selectors.DefaultSelector()
def accept(sock,mask):
conn,addr = sock.accept()
sel.register(conn,selectors.EVENT_READ,read)
def read(conn,mask):
data = conn.recv(1024)
print(data.decode('utf-8'))
response = input(">>>").strip()
conn.send(response.encode('utf-8'))
sock = socket.socket()
sock.bind(("127.0.0.1",9000))
sock.listen(5)
sock.setblocking(False)
sel.register(sock,selectors.EVENT_READ,accept) #注册,绑定套接字对象和函数
while True:
events = sel.select() #监听套接字对象(所有注册的)
for key,mask in events:
callback = key.data #key.data就是套接字对象绑定的那个函数
callback(key.fileobj,mask) #执行函数 #client端
import socket
sock = socket.socket()
sock.connect(("127.0.0.1",9000))
while 1:
data = input('>>>').strip()
sock.send(data.encode('utf-8'))
response = sock.recv(1024)
print(response.decode('utf-8'))

信号驱动IO

简介:两次调用,两次返回。

首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。

异步IO

数据拷贝的时候进程无需阻塞。异步IO不会引起进程阻塞。

当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作

5种IO模型比较

IO模型浅析的更多相关文章

  1. 高性能IO模型浅析

    高性能IO模型浅析 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking  ...

  2. 转 高性能IO模型浅析

    高性能IO模型浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: ( ...

  3. unix io 模型浅析

    POSIX中对同步IO和异步IO的规定: 同步IO操作:引起进程的阻塞直到IO操作完成,异步IO操作:IO操作不会引起进程阻塞 在UNIX下,有5中操作模型: 阻塞IO,非阻塞IO,IO复用,信号驱动 ...

  4. 高性能IO模型浅析(彩图解释)good

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

  5. 高性能IO模型浅析(转)

    转自:http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 是我目前看到的解释IO模型最清晰的文章,当然啦,如果想要详细的进一步了解还是继续啃蓝宝书 ...

  6. 【珍藏】高性能IO模型浅析

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

  7. [转载] 高性能IO模型浅析

    转载自http://www.cnblogs.com/fanzhidongyzby/p/4098546.html 服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(B ...

  8. IO模型浅析-阻塞、非阻塞、IO复用、信号驱动、异步IO、同步IO

    最近看到OVS用户态的代码,在接收内核态信息的时候,使用了Epoll多路复用机制,对其十分不解,于是从网上找了一些资料,学习了一下<UNIX网络变成卷1:套接字联网API>这本书对应的章节 ...

  9. 【转载】高性能IO模型浅析

    服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型. (2)同步非阻塞IO(Non-blocking IO):默认创建的s ...

随机推荐

  1. 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…【字符串+模拟】

    P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He… 题目描述 众所周知,在每一个彗星后都有一只UFO.这些UFO时常来收集地球上的忠诚支持者.不幸的是,他们的飞碟每次出行都 ...

  2. Codeforces Round #395 (Div. 2)(A.思维,B,水)

    A. Taymyr is calling you time limit per test:1 second memory limit per test:256 megabytes input:stan ...

  3. POJ 1804 Brainman(5种解法,好题,【暴力】,【归并排序】,【线段树单点更新】,【树状数组】,【平衡树】)

    Brainman Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 10575   Accepted: 5489 Descrip ...

  4. Linux 文件操作命令-Linux基础环境命令学习笔记

    1.文件目录操作 1)路径识别 a.绝对路径,以根目录开始 b.相对路径,以当前目录开始,不需从根目录开始写起 2)命令 man ls 查看ls命令帮助 cd /var/log 切换进入log目录(. ...

  5. D触发器深入详细介绍(zhuanzai)

    D触发器深入详细介绍,D触发器是对输入时钟脉冲边沿信号敏感的装置.只有在检测到边沿信号,才设置输出信号与输入端D相同.一个基础的电平触发装置是门控D锁存器. D触发器(英文中“D”代表“Data”,“ ...

  6. echarts图表里label文字过长换行的方法

    在做一些图标时,有时会出现显示文字过长的问题,需要将其按照指定的字数换行,像下图这样 而echarts没有提供换行的方法,但是可以使用fomatter方法进行设置,代码如下 formatter: fu ...

  7. ECharts插件的使用

    ECharts插件:官网下载echarts.js开发者可以选择源码.下载地址:http://echarts.baidu.com/download.html 下载之后,echarts.js放在js文件夹 ...

  8. 算法-java代码实现插入排序

    插入排序  

  9. VS的无用文件

    目录:C:\ProgramData\Microsoft Visual Studio\10.0\TraceDebugging 每次调试程序都会产生30MB左右大小的Debug文件.如何禁止? Tools ...

  10. nginx服务器配置/websocket nginx 配置笔记

    server { listen 80; server_name xxx.com; # add_header '*' ; location /u/ { # 反向代理透传客户端ip proxy_set_h ...