Day038--Python--Gevent , IO多路复用
1. 协程:
gevent (遇到IO自动切换)
import gevent
import time
from gevent import monkey; monkey.patch_all() # ;相当于换行 def eat(name):
print('%s eat 1' % name)
# gevent.sleep(1)
time.sleep(2) # gevent 不能识别time.sleep, from gevent import monkey; monkey.patch_all()可解决这个问题, 之后就可以使用time.sleep()了
print('%s eat 2' % name) def play(name):
print('%s play 1' % name)
# gevent.sleep(1)
time.sleep(2)
print('%s play 2' % name) g1 = gevent.spawn(eat, 'alex')
g2 = gevent.spawn(play, name='sylar')
# g1.join()
# g2.join() gevent.joinall([g1, g2])
print('主')
gevent 之同步与异步
from gevent import spawn,joinall,monkey;monkey.patch_all() import time
def task(pid):
"""
Some non-deterministic task
"""
time.sleep(0.5)
print('Task %s done' % pid) def synchronous():
for i in range(10):
task(i) def asynchronous():
g_l=[spawn(task,i) for i in range(10)]
joinall(g_l) if __name__ == '__main__':
print('Synchronous:')
synchronous() print('Asynchronous:')
asynchronous()
gevent 应用列举:
from gevent import monkey;monkey.patch_all()
import gevent
import requests
import time def get_page(url):
print('GET: %s' %url)
response=requests.get(url)
if response.status_code == 200:
print('%d bytes received from %s' %(len(response.text),url)) start_time=time.time()
gevent.joinall([
gevent.spawn(get_page,'https://www.python.org/'),
gevent.spawn(get_page,'https://www.yahoo.com/'),
gevent.spawn(get_page,'https://github.com/'),
])
stop_time=time.time()
print('协程时间>>> %s' %(stop_time-start_time)) # 协程应用:爬虫
print('--------------------------------')
s = time.time()
requests.get('https://www.python.org/')
requests.get('https://www.yahoo.com/')
requests.get('https://github.com/')
t = time.time()
print('串行时间>>',t-s)
协程应用: 爬虫
参考: https://www.cnblogs.com/clschao/articles/9712056.html#_label4
阻塞 IO


import socket
import time server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 8083))
server.listen(5)
print('你看看卡在哪')
while 1:
conn, addr = server.accept() # 等待客户端连接, 阻塞住
print('来自%s的链接请求' % addr)
time.sleep(0.1)
阻塞IO
非阻塞 IO模型

import socket
import time server=socket.socket()
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1',8083))
server.listen(5)
print('你看看卡在哪')
server.setblocking(False) # 不再阻塞等待
while 1:
try:
conn, addr = server.accept()
print('来自%s的链接请求'%addr)
except BlockingIOError:
print('去买点药')
time.sleep(0.1)
import socket
import time server=socket.socket()
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1',8083))
server.listen(5)
print('你看看卡在哪')
server.setblocking(False)
rlist = []
rl = []
while 1:
try:
conn, addr = server.accept()
print(addr)
rlist.append(conn)
print('来自%s:%s的链接请求'%(addr[0],addr[1]))
except BlockingIOError:
print('去买点药') time.sleep(0.1) # 防止死循环一直高度占用CPU
print('rlist',rlist,len(rlist))
for con in rlist:
try:
from_client_msg = con.recv(1024)
except BlockingIOError:
continue
except ConnectionResetError:
con.close()
rl.append(con)
print('>>>>',rl)
for remove_con in rl:
rlist.remove(remove_con)
rl.clear()
阻塞IO的socket服务端
import socket
import time ip_port = ('127.0.0.1',8083) client = socket.socket() client.connect(ip_port) while 1: client.send(b'dayangge henweisuo ')
time.sleep(0.1)
阻塞IO的socket客户端
# 服务端
import socket
import time server=socket.socket()
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1',8083))
server.listen(5) server.setblocking(False) #设置不阻塞
r_list=[] #用来存储所有来请求server端的conn连接
w_list={} #用来存储所有已经有了请求数据的conn的请求数据 while 1:
try:
conn,addr=server.accept() #不阻塞,会报错
r_list.append(conn) #为了将连接保存起来,不然下次循环的时候,上一次的连接就没有了
except BlockingIOError:
# 强调强调强调:!!!非阻塞IO的精髓在于完全没有阻塞!!!
# time.sleep(0.5) # 打开该行注释纯属为了方便查看效果
print('在做其他的事情')
print('rlist: ',len(r_list))
print('wlist: ',len(w_list)) # 遍历读列表,依次取出套接字读取内容
del_rlist=[] #用来存储删除的conn连接
for conn in r_list:
try:
data=conn.recv(1024) #不阻塞,会报错
if not data: #当一个客户端暴力关闭的时候,会一直接收b'',别忘了判断一下数据
conn.close()
del_rlist.append(conn)
continue
w_list[conn]=data.upper()
except BlockingIOError: # 没有收成功,则继续检索下一个套接字的接收
continue
except ConnectionResetError: # 当前套接字出异常,则关闭,然后加入删除列表,等待被清除
conn.close()
del_rlist.append(conn) # 遍历写列表,依次取出套接字发送内容
print('wlist: ', len(w_list))
del_wlist=[]
for conn,data in w_list.items():
try:
conn.send(data)
del_wlist.append(conn)
except BlockingIOError:
continue # 清理无用的套接字,无需再监听它们的IO操作
for conn in del_rlist:
r_list.remove(conn)
#del_rlist.clear() #清空列表中保存的已经删除的内容
for conn in del_wlist:
w_list.pop(conn)
#del_wlist.clear()
完整版的非阻塞IO服务端
import socket
import os
import time
import threading
client=socket.socket()
client.connect(('127.0.0.1',8083)) while 1:
res=('%s hello' %os.getpid()).encode('utf-8')
client.send(res)
data=client.recv(1024) print(data.decode('utf-8')) ##多线程的客户端请求版本
# def func():
# sk = socket.socket()
# sk.connect(('127.0.0.1',9000))
# sk.send(b'hello')
# time.sleep(1)
# print(sk.recv(1024))
# sk.close()
#
# for i in range(20):
# threading.Thread(target=func).start()
完整版的非阻塞IO客户端
select IO多路复用 (重点)

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。 这个图和blockingIO的图其实并没有太大的不同,事实上还更差一些。因为它不仅阻塞了还多需要使用两个系统调用(select和recvfrom),而blockingIO只调用了一个系统调用(recvfrom),当只有一个连接请求的时候,这个模型还不如阻塞IO效率高。但是,用select的优势在于它可以同时处理多个connection,而阻塞IO那里不能,我不管阻塞不阻塞,你所有的连接包括recv等操作,我都帮你监听着(以什么形式监听的呢?先不要考虑,下面会讲的~~),其中任何一个有变动(有链接,有数据),我就告诉你用户,那么你就可以去调用这个数据了,这就是他的NB之处。这个IO多路复用模型机制是操作系统帮我们提供的,在windows上有这么个机制叫做select,那么如果我们想通过自己写代码来控制这个机制或者自己写这么个机制,我们可以使用python中的select模块来完成上面这一系列代理的行为。在一切皆文件的unix下,这些可以接收数据的对象或者连接,都叫做文件描述符fd
IO多路复用
import select fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout]) 参数: 可接受四个参数(前三个必须)
rlist: wait until ready for reading #等待读的对象,你需要监听的需要获取数据的对象列表
wlist: wait until ready for writing #等待写的对象,你需要写一些内容的时候,input等等,也就是说我会循环他看看是否有需要发送的消息,如果有我取出这个对象的消息并发送出去,一般用不到,这里我们也给一个[]。
xlist: wait for an “exceptional condition” #等待异常的对象,一些额外的情况,一般用不到,但是必须传,那么我们就给他一个[]。
timeout: 超时时间
当超时时间 = n(正整数)时,那么如果监听的句柄均无任何变化,则select会阻塞n秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。
返回值:三个列表与上面的三个参数列表是对应的
select方法用来监视文件描述符(当文件描述符条件不满足时,select会阻塞),当某个文件描述符状态改变后,会返回三个列表
1、当参数1 序列中的fd满足“可读”条件时,则获取发生变化的fd并添加到fd_r_list中
2、当参数2 序列中含有fd时,则将该序列中所有的fd添加到 fd_w_list中
3、当参数3 序列中的fd发生错误时,则将该发生错误的fd添加到 fd_e_list中
4、当超时时间为空,则select会一直阻塞,直到监听的句柄发生变化
介绍
#服务端
from socket import *
import select
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',8093))
server.listen(5)
# 设置为非阻塞
server.setblocking(False) # 初始化将服务端socket对象加入监听列表,后面还要动态添加一些conn连接对象,当accept的时候sk就有感应,当recv的时候conn就有动静
rlist=[server,]
rdata = {} #存放客户端发送过来的消息 wlist=[] #等待写对象
wdata={} #存放要返回给客户端的消息 print('预备!监听!!!')
count = 0 #写着计数用的,为了看实验效果用的,没用
while True:
# 开始 select 监听,对rlist中的服务端server进行监听,select函数阻塞进程,直到rlist中的套接字被触发(在此例中,套接字接收到客户端发来的握手信号,从而变得可读,满足select函数的“可读”条件),被触发的(有动静的)套接字(服务器套接字)返回给了rl这个返回值里面;
rl,wl,xl=select.select(rlist,wlist,[],0.5)
print('%s 次数>>'%(count),wl)
count = count + 1
# 对rl进行循环判断是否有客户端连接进来,当有客户端连接进来时select将触发
for sock in rl:
# 判断当前触发的是不是socket对象, 当触发的对象是socket对象时,说明有新客户端accept连接进来了
if sock == server:
# 接收客户端的连接, 获取客户端对象和客户端地址信息
conn,addr=sock.accept()
#把新的客户端连接加入到监听列表中,当客户端的连接有接收消息的时候,select将被触发,会知道这个连接有动静,有消息,那么返回给rl这个返回值列表里面。
rlist.append(conn)
else:
# 由于客户端连接进来时socket接收客户端连接请求,将客户端连接加入到了监听列表中(rlist),客户端发送消息的时候这个连接将触发
# 所以判断是否是客户端连接对象触发
try:
data=sock.recv(1024)
#没有数据的时候,我们将这个连接关闭掉,并从监听列表中移除
if not data:
sock.close()
rlist.remove(sock)
continue
print("received {0} from client {1}".format(data.decode(), sock))
#将接受到的客户端的消息保存下来
rdata[sock] = data.decode() #将客户端连接对象和这个对象接收到的消息加工成返回消息,并添加到wdata这个字典里面
wdata[sock]=data.upper()
#需要给这个客户端回复消息的时候,我们将这个连接添加到wlist写监听列表中
wlist.append(sock)
#如果这个连接出错了,客户端暴力断开了(注意,我还没有接收他的消息,或者接收他的消息的过程中出错了)
except Exception:
#关闭这个连接
sock.close()
#在监听列表中将他移除,因为不管什么原因,它毕竟是断开了,没必要再监听它了
rlist.remove(sock)
# 如果现在没有客户端请求连接,也没有客户端发送消息时,开始对发送消息列表进行处理,是否需要发送消息
for sock in wl:
sock.send(wdata[sock])
wlist.remove(sock)
wdata.pop(sock) # #将一次select监听列表中有接收数据的conn对象所接收到的消息打印一下
# for k,v in rdata.items():
# print(k,'发来的消息是:',v)
# #清空接收到的消息
# rdata.clear() ---------------------------------------
#客户端
from socket import * client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8093)) while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8')) client.close() select网络IO模型的示例代码
select网络IO模型示例代码
select IO多路复用 异步
异步IO asyncio (很厉害, 高并发)
Day038--Python--Gevent , IO多路复用的更多相关文章
- {python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块
python之IO多路复用 阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 ...
- day36 python学习gevent io 多路复用 socketserver *****
---恢复内容开始--- gevent 1.切换+保存状态 2.检测单线程下任务的IO,实现遇到IO自动切换 Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在geven ...
- python中IO多路复用、协程
一.IO多路复用 IO多路复用:检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写) import socket def get_data(key): client ...
- python之IO多路复用
在python的网络编程里,socetserver是个重要的内置模块,其在内部其实就是利用了I/O多路复用.多线程和多进程技术,实现了并发通信.与多进程和多线程相比,I/O多路复用的系统开销小,系统不 ...
- 【python】-- IO多路复用(select、poll、epoll)介绍及实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- 09 Python之IO多路复用
四种常见IO模型 阻塞IO(blocking IO).非阻塞IO(nonblocking IO).IO多路复用(IOmultiplexing).异步IO(asynchronous IO) IO发生时涉 ...
- Python poll IO多路复用
一.poll介绍 poll本质上和select没有区别,只是没有了最大连接数(linux上默认1024个)的限制,原因是它基于链表存储的. 本人的另一篇博客讲了 python select : ht ...
- Python select IO多路复用
一.select介绍 Python的select()函数是底层操作系统实现的直接接口.它监视套接字,打开文件和管道(任何带有返回有效文件描述符的fileno()方法),直到它们变得可读或可写,或者发生 ...
- Python之路,Day9 - 线程、进程、协程和IO多路复用
参考博客: 线程.进程.协程: http://www.cnblogs.com/wupeiqi/articles/5040827.html http://www.cnblogs.com/alex3714 ...
- Python进程、线程、协程及IO多路复用
详情戳击下方链接 Python之进程.线程.协程 python之IO多路复用
随机推荐
- nginx配置proxy_pass URL末尾加与不加/(斜线)的区别
nginx在配置proxy_pass的时候 URL结尾加斜线(/)与不加的区别和注意事项 假设访问路径的 /pss/bill.html 加/斜线的情况 location /pss/ { proxy_p ...
- mysql安装出现问题(The service already exists)
1.管理员身份运行cmd(系统win10) 2.输入命令cd /d F:\mysql-5.7.19-win32\bin(此为mysql要安装的目录) 3.输入安装命令mysqld install 出现 ...
- 从0开始的Python学习006流程控制
流程控制语句 Python中有三种控制流程语句: if.for.和while. if语句 使用if语句来校验一个条件,如果条件为真(True),运行if-块,如果为假(False),运行else-块. ...
- 利用ZYNQ SOC快速打开算法验证通路(3)——PS端DMA缓存数据到PS端DDR
上篇该系列博文中讲述W5500接收到上位机传输的数据,此后需要将数据缓存起来.当数据量较大或者其他数据带宽较高的情况下,片上缓存(OCM)已无法满足需求,这时需要将大量数据保存在外挂的DDR SDRA ...
- go语言学习-常用命令(四)
go常用命令 go get:获取远程包(得装git) go run:直接运行程序(写代码时调试用) go build:测试编译,检查是否有编译错误 go fmt:格式化代码(一般不咋用,IDE都自带了 ...
- 【PAT】B1016 部分A+B
水题 以字符和字符串形式储存输入,比较,计算出两个个数的D的个数,用for循环拼成P,相加得出结果 #include<stdio.h> int main(){ char A[20],DA, ...
- #020PAT 没整明白的题L1-009 N个数求和 (20 分)
后面的测试点过不去,两个错误一个超时. 目前未解决 L1-009 N个数求和 (20 分) 本题的要求很简单,就是求N个数字的和.麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和 ...
- Python简单的多线程demo:装逼写法
用面向对象来写多线程: import threading class MyThread(threading.Thread): def __init__(self, n): super(MyThread ...
- top后台执行显示:top: failed tty get 错误
通过其他程序或脚本在非交互式模式下调用top命令,经常会出现: top: failed tty get 错误 解决办法:加个-b 选项皆可 -b : Batch mode operation ...
- 入门 Webpack,一篇就够了
阅读本文之前,先看下面这个webpack的配置文件,如果每一项你都懂,那本文能带给你的收获也许就比较有限,你可以快速浏览或直接跳过:如果你和十天前的我一样,对很多选项存在着疑惑,那花一段时间慢慢阅读本 ...