第七章|7.4并发编程| I/O模型
I/O模型
协程是单线程下的并发,并不是对性能都有所提升,一定是监测单个线程下的多个任务的I/O,遇到I/O不要让它阻塞,给它自动切换到其他任务去,这样就能提高单个线程下的运行效率。--->>用gevent模块来实现了,gevent是怎么检测I/O行为的呢,gevent监测行为,遇到I/O自动切换到其他任务去,并发效果有了性能也提升了。
讲I/O模型就是为了你自己去实现一个gevent模式。
同步异步(提交任务的方式),同步调用是提交完任务在原地等着结果拿到结果后再运行下行代码;异步调用是提交完就不管了接着往下执行,异步通常跟回调机制连用,我提交完一个任务,这个任务运行完后会自动触发回调函数运行把结果交给它。
同步不等于阻塞。同步只是提交任务的方式,任务是否阻塞我不管,我就在这等着只要运行完了才往下走;阻塞是遇到I/O了,你自己没有处理,操作系统会剥夺你使用cpu给别人用。我们要解决的就是一个线程下可能要干的活很多,你不要遇到I/O就卡在原地了,我能够检测到它确实是一个I/O行为好切换到其他任务;非阻塞和阻塞正好相反。
遇到I/O为什么会卡那呢,我们要研究的是套接字I/O行为,实现服务端尽可能多的支持并发、性能提升起来,研究的主要是网络I/O。
服务端套接字accept(等着别人连接,给你发一个数据,会让你感觉明显的等),recv(收消息) ,send(传消息,并不会让你感觉等)都属于I/O阻塞行为;
recv经历两个阶段:waite等数据(从自己的缓存数据收消息,从客户端到服务端,耗时最长);从操作系统缓存内存copy到应用程序内存;accept和recv是一个的都要经历这两个阶段。send是应用程序把自己的数据copy到操作系统内存,本地copy的速度非常快,它只经历这一个阶段,然后接下来由操作系统去干。
四种I/O模型不同之处就是对这两个阶段的处理不一样。
阻塞I/O
recvfrom要经历wait for data和copy data 两个阶段才算执行完毕,这就就是阻塞I/O。
#阻塞I/O 没有并发、遇到阻塞就等着
from socket import *
from threading import Thread #可利用多线程实现并发,让主线程干accept的活
def communicate(conn):
while True: #通信循环
try:
data = conn.recv() #等待,等着收消息,客户端要产生数据
if not data: break
conn.send(data.upper())
except ConnectionResetError:
break conn.close() server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',))
server.listen() while True: #连接循环
print('starting...')
conn, addr = server.accept() #wait阶段主要卡在这里;accept等着对方连我,别人给我发消息才算过去了
print(addr)#客户端来了 t=Thread(target=communicate,args=(conn,)) #让主线程干accept的活,每来一个链接发起个线程让它干通信的活;没有解决I/O阻塞,起了多个线程各个运行互不影响;
t.start() #线程池保证机器在健康的状态下运行; server.close()
from socket import * client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',)) #发连接 while True:
msg=input('>>: ').strip() #输消息
if not msg:continue
client.send(msg.encode('utf-8')) #发消息
data=client.recv()
print(data.decode('utf-8')) #收消息
client.close()
非阻塞I/O
单线程下的多并发
监测单线程下的I/O,帮你自动切换到另外一个任务去运行,wait和copy阶段是怎么处理的呢,跟gevent一样。
由应用程序发起个系统调用recvfrom打算收一个消息,发给操作系统内核,在阻塞I/O它会让你一直在原地等着。非阻塞I/O,发给操作系统内核,操作系统会说没有数据准备好,返回个信号给你的应用程序,应用程序就可以先去干其他的活,不用再等了,干一会其他的活再问问来了没,这样循环。当操作系统准备好了之后,把数据由系统的缓冲区copy到应用程序。waite阶段是你可以利用的可以干其他的活。
from socket import * server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',))
server.listen()
server.setblocking(False) #True是阻塞,False是非阻塞
print('starting...') rlist=[] #有连接来就不停的加
wlist=[] #send在数据量大的情况下也会阻塞
while True:
try:
conn, addr = server.accept() #2问操作系统有没有数据 ;服务端可以不停的建链接
rlist.append(conn) #有链接就建链接,每建立一个链接就放在列表里
print(rlist)
except BlockingIOError: #3捕捉这个异常就是那个操作系统发的信号,拿到这个异常然后干其他的活;利用accept那个waite阶段
#print('干其他的活') #没有数据来就可以干其他的活了, #收消息
del_rlist = [] #
for conn in rlist: #取出conn去收消息
try:
data=conn.recv()
if not data: #收空消息,就跳过
del_rlist.append(conn)
continue
wlist.append((conn,data.upper())) #存放套接字以及准备发的数据
except BlockingIOError: #捕捉异常,第一个套接字没有,可能下一个就来了
continue #跳过
except Exception: #可能套接字出现异常,客户端单方面的把连接断开;
conn.close() #把么有用的给回收掉,del_rlist从删除它,不能在循环过程中删除
del_rlist.append(conn)# #发消息
del_wlist=[]
for item in wlist:
try:
conn=item[]#拿到conn对象
data=item[]#拿到数据
conn.send(data) #缓冲区大小不能无线的大,send在数据量过大的情况下也会阻塞
del_wlist.append(item)
except BlockingIOError: #出异常了是内存满了,没抛异常证明发成功了
pass for item in del_wlist:#发成功了把你删了
wlist.remove(item) for conn in del_rlist: #把没有数据发来的conn给删除掉
rlist.remove(conn) server.close()
from socket import * client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',)) while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv()
print(data.decode('utf-8')) client.close()
多路复用I/O模型
IO multiplexing这个词可能有点陌生,但是如果我说select/epoll,大概就都能明白了。有些地方也称这种IO方式为事件驱动IO。
直接发select(它是多路复用模型的一种,相当于一个中介,套接字有没有准备好由中介去问了,问操作系统数据有没有准备好,没有准备好它就在那等着;等到套接字数据准备好之后由服务端去发起个系统调用,是没有waite阶段的。)
多路复用经历了3个阶段,多了一个中间交互的阶段,性能还不如阻塞I/O,针对一个套接字;select高性能在于它可以监测多个套接字,而阻塞I/O只能有一个。
服务端的套接字有几类?conn,addr=server.accept()建链接;还有一个是建好链接的那个conn,负责recv和send操作。
from socket import *
import select server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',))
server.listen()
server.setblocking(False)
print('starting...') rlist=[server,] #专门存收消息的 server调accept用来收;还有一个种是建完链接之后才能拿到的conn套接字;初始状态就是放server,后来可以放conn
wlist=[] #存发的数据
wdata={} while True:
rl,wl,xl=select.select(rlist,wlist,[],0.5) #去问操作系统套接字准备好没有;[]表示出异常的列表;0.5表示每隔0.5s去问一次;
print('rl',rl) #rlist会不断存conn和server一堆;wlist存一堆conn,缓冲区一旦没满证明就可以发了
print('wl',wl) #wl是可以往缓冲区send值的这种套接字 for sock in rl: #读列表里边是干accept和recv的活
if sock == server: #干链接的活
conn,addr=sock.accept() #拿到conn的活
rlist.append(conn)
else:
try:
data=sock.recv(1024) #拿到数据,加到那个列表里边
if not data: #针对linux系统
sock.close()
rlist.remove(sock) #这个套接字不要监测了
continue
wlist.append(sock) #把套接字加进去,还有它配套的数据
wdata[sock]=data.upper()
except Exception:#有可能客户端单方面把链接断开
sock.close()
rlist.remove(sock) ##rlist没有必要监测这个套接字了 for sock in wl: #只能执行send操作
data=wdata[sock]
sock.send(data) #传数据
wlist.remove(sock)#传完之后就把它给删了
wdata.pop(sock) server.close()
from socket import * client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',)) while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv()
print(data.decode('utf-8')) client.close()
异步I/O模型
首先还是发一个系统调用,发送给操作系统内核之后,立马返回,返回之后就可以干其他活了。经历了waite和copy阶段,最后把数据直接丢给它。向操作系统说我要一个数据,数据请求发出去之后,操作系统你给我准备好数据,准备好之后给我送过来,这就是一种异步的方式。比阻塞I/O高,发起请求就直接返回了,没有经历waite和copy阶段
那是操作系统干的活;比起非阻塞I/O也高了,不用循环的去问了,它是什么时候好什么时候给我送回来。
第七章|7.4并发编程| I/O模型的更多相关文章
- Java并发编程-Java内存模型
JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ...
- x86-TSO : 适用于x86体系架构并发编程的内存模型
Abstract : 如今大数据,云计算,分布式系统等对算力要求高的方向如火如荼.提升计算机算力的一个低成本方法是增加CPU核心,而不是提高单个硬件工作效率. 这就要求软件开发者们能准确,熟悉地运用高 ...
- 【并发编程】- 内存模型(针对JSR-133内存模型)篇
并发编程模型 1.两个关键问题 1)线程之间如何通信 共享内存 程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信 消息传递 程之间没有公共状态,线程之间必须通过发送消息来显式进行通信 ...
- Java并发编程、内存模型与Volatile
http://www.importnew.com/24082.html volatile关键字 http://www.importnew.com/16142.html ConcurrentHash ...
- 并发编程-Java内存模型
将之前看过的关于并发编程的东西总结记录一下,本文简单记录Java内存模型的相关知识. 1. 并发编程两个关键问题 并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步. (1)在命令式 ...
- 第七章 资源在Windows编程中的应用 P157 7-8
资源在基于SDK的程序设计中的应用实验 一.实验目的 1.掌握各种资源的应用及资源应用的程序设计方法. 二.实验内容及步骤 实验任务 1.熟悉菜单资源的创建过程: 2.熟悉位图资源的创建: 3.熟 ...
- Python之网络编程之并发编程的IO模型,
了解新知识之前需要知道的一些知识 同步(synchronous):一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行 #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调 ...
- 【并发编程】IO模型
一.要点回顾 为了更好地了解IO模型,我们需要先回顾下几个概念:同步.异步.阻塞.非阻塞 同步: 一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行.就是在发出一个功能调用时,在没 ...
- 并发编程(三) IO模型
五 IO模型 常用的IO模型有4种: 阻塞IO 非阻塞IO IO多路复用 异步IO 不常用的有: 驱动信号 5.1 阻塞IO.非阻塞IO 阻塞IO:进程不能做其他的事情 非阻塞IO:等待数据无阻塞 阻 ...
随机推荐
- C# ffmpeg工具将视频转为SWF格式
1.下载ffmpeg工具 using System; using System.Collections; using System.Configuration; using System.Data; ...
- Python3之网络爬虫<0>初级
由于Python3合并URLib与URLlib2统一为URLlib,Python3将urlopen方法放在了urllib.request对象下. 官方文档:https://docs.python.or ...
- JavaScript之表格操作(二)创建表格病填充表格数据
//创建表格 var tableOptions = { way: "insertBefore", //insertBefore,append positionId: "d ...
- luogu P1437 [HNOI2004]尻♂砖块
传送门 想明白了其实不难 强行瞎扯 这题的限制比较烦,导致了一行行转移几乎不能做(吧) 那么一列列转移呢? 设\(f_{i,j,k}\)表示前\(i\)列,取\(j\)个,其中第\(i\)列取从上往下 ...
- POJ1258 Agri-Net【最小生成树】
题意: 有n个农场,已知这n个农场都互相相通,有一定的距离,现在每个农场需要装光纤,问怎么安装光纤能将所有农场都连通起来,并且要使光纤距离最小,输出安装光纤的总距离. 思路: 又是一个最小生成树,因为 ...
- while与for不能互换的地方
- python - 自定制property/property的延时计算
自定制prooerty: #模拟@property 实现将类的函数属性变成类属性: #定义描述符 class msf(): def __init__(self,obj): self.obj = obj ...
- python - 装饰器+描述符(给类添加属性且属性类型审核)
装饰器+描述符 实现给一个类添加属性且对添加的时,对属性进行类型审核: def zsq(**kwargs): def fun(obj): for i,j in kwargs.items(): seta ...
- 【Python】多线程-线程池使用
1.学习目标 线程池使用 2.编程思路 2.1 代码原理 线程池是预先创建线程的一种技术.线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中.这些线程都是处于睡眠状态,即均为启动,不消耗 ...
- GCC的符号可见性——解决多个库同名符号冲突问题
引用自:https://github.com/wwbmmm/blog/wiki/gcc_visibility 问题 最近项目遇到一些问题,场景如下 主程序依赖了两个库libA的funcA函数和libB ...