一、要点回顾

为了更好地了解IO模型,我们需要先回顾下几个概念:同步、异步、阻塞、非阻塞

同步:

一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行。就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。按照这个定义,其实绝大多数函数都是同步调用。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。

异步:

一个进程在执行某个任务时,其他进程不必等待其执行完毕就能开始执行。当一个异步功能调用发出后,调用者不能立刻得到结果。当该异步功能完成后,通过状态、通知或回调来通知调用者。如果异步功能用状态来通知。那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一 种很严重的错误)。如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。

阻塞:

阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。函数只有在得到结果之后才会将阻塞的线程激活。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。

非阻塞:

不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程。

总结:

  1. 同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成;
  2. 阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程。

二、阻塞IO

阻塞IO模型流程如下:

阻塞IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据)都阻塞了。

实际上,除非特别指定,几乎所有的IO接口 ( 包括socket接口 ) 都是阻塞型的。这给网络编程带来了一个很大的问题,如在调用recv(1024)的同时,线程将被阻塞,在此期间,线程将无法执行任何运算或响应任何的网络请求。

 from socket import *
server = socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
print('server start...')
while True:
conn,addr = server.accept() #IO操作 在这accept的时候不能干recv的活
print(addr)
while True:
try:
data = conn.recv(1024) #IO操作
conn.send(data.upper())
except Exception:
break
conn.close()
server.close()

服务端

 from socket import *
client = socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
cmd = input('>>:').strip()
if not cmd:continue
client.send(cmd.encode('utf-8'))
data = client.recv(1024)
print('接受的是:%s'%data.decode('utf-8'))
client.close()

客户端

一旦阻塞了就在那卡着直到等到数据已经到了操作系统,操作系统再从内核拷贝给应用程序阻塞IO在那两个阶段全都阻塞住了。这样要想实现并发,可以使用多线程,多进程或线程池等方式。而多线程一旦连接过多,线程过多,会极大消耗系统资源。

三、非阻塞IO

recvfrom发起系统调用后, 如果数据未准备好, 内核也会立即返回, 这样用户程序可以不用阻塞继续执行后面的操作,但需要循环向内核发出系统调用来查询数据是否准备好,一旦数据准备好,recvfrom便会发起系统调用从内核空间拷贝数据,拷贝数据的过程是阻塞的。

 from socket import *

 server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',8099))
server.listen(5)
server.setblocking(False) rlist=[]
wlist=[]
while True:
try:
conn, addr = server.accept()
rlist.append(conn)
print(rlist) except BlockingIOError:
del_rlist=[]
for sock in rlist:
try:
data=sock.recv(1024)
if not data:
del_rlist.append(sock)
wlist.append((sock,data.upper()))
except BlockingIOError:
continue
except Exception:
sock.close()
del_rlist.append(sock) del_wlist=[]
for item in wlist:
try:
sock = item[0]
data = item[1]
sock.send(data)
del_wlist.append(item)
except BlockingIOError:
pass for item in del_wlist:
wlist.remove(item) for sock in del_rlist:
rlist.remove(sock) server.close()

服务端

 from socket import *
c=socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8080)) while True:
msg=input('>>: ')
if not msg:continue
c.send(msg.encode('utf-8'))
data=c.recv(1024)
print(data.decode('utf-8'))

客户端

这样也存在一个缺点:大量进行系统调用, 会极大消耗cpu资源; 同时由于查询间隔, 将不能及时的获取数据。

四、多路复用IO

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom),而blocking IO只调用了一个系统调用(recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

   强调:

1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

    结论: select的优势在于可以处理多个连接,不适用于单个连接

 from socket import *
import select
server = socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8081))
server.setblocking(False) #设置socket的套接字为非阻塞的
server.listen(5)
print('start running....')
read_l = [server,] #因为不只就那么一个列表要检测。所以不要在参数里面定死了
while True:
r_l,w_l,x_l = select.select(read_l,[],[]) #select()方法有四个参数
print(r_l) #一开始服务端运行的时候,就等着,当你客户端一链接的时候,他就
# 检测到有数据了(检测那个数据准备好了)
for obj in r_l:
if obj == server:
conn,addr = obj.accept() #accept要经历两个阶段,但是程序如果走到这一步,那肯定是数据准备好了
#当数据已经准备好的时候,accept就只经历一个copy数据的阶段了
# print(addr)
read_l.append(conn) #在监听一下conn套接字(这时候已经监听了两个了:分别是accept,conn)
else:
data = obj.recv(1024) # 此时的obj=conn
obj.send(data.upper())
# obj.close()
# server.close()

服务端

 from socket import *
import select
client = socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8081))
while True:
cmd = input('>>:')
client.send(cmd.encode('utf-8'))
data = client.recv(1024)
print('接收的是:%s'%data.decode('utf-8'))
client.close()

客户端

优点:

相比其他模型,使用select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,同时能够为多客户端提供服务。如果试图建立一个简单的事件驱动的服务器程序,这个模型有一定的参考价值。select模块用select方法检测那个套接字准备好了,也就是收没收到数据(而我们的非阻塞IO你不知道那个套接字准备好了,那么用select模块就能解决这个问题)。select还可以检测多个套接字,所以select比非阻塞IO的效率高。

缺点:

首先select()接口并不是实现“事件驱动”的最好选择。因为当需要探测的句柄值较大时,select()接口本身需要消耗大量时间去轮询各个句柄。很多操作系统提供了更为高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。
如果需要实现更高效的服务器程序,类似epoll这样的接口更被推荐。遗憾的是不同的操作系统特供的epoll接口有很大差异,所以使用类似于epoll的接口实现具有较好跨平台能力的服务器会比较困难。其次,该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。

五、异步IO

未完待续……

【并发编程】IO模型的更多相关文章

  1. 4.6 并发编程/IO模型

    并发编程/IO模型 背景概念 IO模型概念 IO模型分类 阻塞IO  (blocking IO) 特点: 两个阶段(等待数据和拷贝数据两个阶段)都被block 设置 server.setsockopt ...

  2. python 并发编程 io模型 目录

    python 并发编程 IO模型介绍 python 并发编程 socket 服务端 客户端 阻塞io行为 python 并发编程 阻塞IO模型 python 并发编程 非阻塞IO模型 python 并 ...

  3. Python Web学习笔记之并发编程IO模型

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

  4. Python之并发编程-IO模型

    目录 一.IO模型介绍二.阻塞IO(blocking IO)三.非阻塞IO(non-blocking IO)四.多路复用IO(IO multiplexing)五.异步IO(Asynchronous I ...

  5. 并发编程 - io模型 - 总结

    1.提交任务得方式: 同步:提交完任务,等结果,执行下一个任务 异步:提交完,接着执行,异步 + 回调 异步不等结果,提交完任务,任务执行完后,会自动触发回调函数2.同步不等于阻塞: 阻塞:遇到io, ...

  6. python并发编程&IO模型

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

  7. 并发编程——IO模型

    前言 同步(synchronous):一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行 #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回.按照这个定义, ...

  8. 并发编程——IO模型详解

    ​ 我是一个Python技术小白,对于我而言,多任务处理一般就借助于多进程以及多线程的方式,在多任务处理中如果涉及到IO操作,则会接触到同步.异步.阻塞.非阻塞等相关概念,当然也是并发编程的基础. ​ ...

  9. 15 并发编程-(IO模型)

    一.IO模型介绍 1.阻塞与非阻塞指的是程序的两种运行状态 阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源 非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种 ...

  10. 并发编程——IO模型(6)

    1.IO模型分类 同步IO #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回.按照这个定义,其实绝大多数函数都是同步调用.但是一般而言,我们在说同步.异步的时候,特指那些需要 ...

随机推荐

  1. 黄聪:什么是XSS攻击

    XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中.比如这些代码包括HTML代码和客户端脚本.攻击者利用XSS漏洞旁路掉访问控制——例如同源 ...

  2. 常用HDFS操作命令

    前一段时间频繁使用HDFS,又收集到了一些命令,在这儿分享出来,大数据的框架及设计原理方面的理论文章暂时还没有时间总结,后面有时间逐渐整理发出来. 注:在使用命令时,可以使用 hadoop fs,如果 ...

  3. python3之os、sys

    os模块 # 显示当前使用平台:"nt":windows;"posix":Linux >>> os.name 'nt' # 当前工作目录 &g ...

  4. Twisted网络库编程实例

    于这一周看了python的第三方网络库Twisted,英文看的头比较大,想看英文的话点击这里.如果英文很烂,可以看中文,这里.总的来说我了解到的主要包括以下三个东东:Factory.protocol和 ...

  5. LeetCode contest-95[876,877,👁878]

    876. Middle of the Linked List first submission # Definition for singly-linked list. # class ListNod ...

  6. 2.pandas数据清洗

    pandas是用于数据清洗的库,安装配置pandas需要配置许多依赖的库,而且安装十分麻烦. 解决方法:可以用Anaconda为开发环境,Anaconda内置了许多有关数据清洗和算法的库. 1.安装p ...

  7. java中Class.getMethod方法

    Method Class.getMethod(String name, Class<?>... parameterTypes)的作用是获得对象所声明的公开方法 该方法的第一个参数name是 ...

  8. extentReport生成测试报告

    之前在使用extentReport生成测试报告的时候,没有加载到相关的css,经检查为下面两个文件没有正确加载 后改变配置,加载本地的css和js文件,目前测试报告正确显示 1.创建TestNg的Re ...

  9. AI大道理头尾标识

    标题 点击上方“AI大道理”,选择“置顶”公众号 重磅干货,深入讲解AI大道理 —————— 正文 —————— 浅谈则止,深入理解AI大道理 扫描下方“AI大道理”,选择“关注”公众号 欢迎加入!

  10. HDU 5828 Rikka with Sequence(线段树区间加开根求和)

    Problem DescriptionAs we know, Rikka is poor at math. Yuta is worrying about this situation, so he g ...