python 模拟io_Python 协程、IO模型

1.协程(单线程实现并发)

2.I/0模型

2.1阻塞I/O

2.2非阻塞I/O

知识点一:协程

协程的目的:是想要在单线程下实现并发(并发看起来是同时运行的)

并发=多个任务间切换+保存状态(正常情况都是由操作系统来控制的)

一般情况下都是由操作系统来控制的,现在要实现的就是遇到I/o自己来切换,也叫并发

优点:在应用程序级别速度要远远高于操作系统的切换

缺点:多个任务一旦有一个阻塞没有切,整个线程都会阻塞原地,该线程内的其他任务

都不能执行

一旦引入协程,就需要检测单线程下所有的IO行为,实际遇到IO就切换,

少一个都不行,一旦一个任务阻塞了,整个线程就阻塞了,其他的任务即便是

可以计算,但是也无法运行了

一个程序没有遇到IO也切,反而会降低效率,应该找到一种让程序遇到IO切,才能提高效率

gevent模块:模拟识别IO阻塞

#gevent模块:模拟识别IO阻塞

importgeventfrom gevent import monkey,spawn;monkey.patch_all() #pathch_all()打补丁就是让gevent能识别所有的IO

from threading importcurrent_threadimporttimedefeat():print('%s eat 1'%current_thread().name)#gevent.sleep(2) #默认只能识别自己模块的Io行为

time.sleep(3)print('%s eat 2'%current_thread().name)defplay():print('%s play 1'%current_thread().name)#gevent.sleep(1)

time.sleep(1)print('%s play 2'%current_thread().name)#创建协程对象

g1=spawn(eat,) #spawn(函数名,参数1...) 都是传给函数eat的

g2=spawn(play,)print(current_thread().name)

g1.join()#等待g1结束

g2.join() #等待g2结束

'''输出结果:

MainThread

DummyThread-1 eat 1 #假线程

DummyThread-2 play 1

DummyThread-2 play 2

DummyThread-1 eat 2'''

'''而time.sleep(2)或其他的阻塞,gevent是不能直接识别的需要用下面一行代码,打补丁,就可以识别了

from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前

或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头

gevent应用实例一:利用gevent模块改写socket服务端实现:单线程IO切换自动切换

#服务端

from gevent importspawn,monkey;monkey.patch_all()from socket import *

from threading importThreaddeftalk(conn):whileTrue:try:

data=conn.recv(1024)if len(data) == 0:breakconn.send(data.upper())exceptConnectionResetError:breakconn.close()def server(ip,port,backlog=5):

server=socket(AF_INET, SOCK_STREAM)

server.bind((ip, port))

server.listen(backlog)print('starting...')whileTrue:

conn, addr=server.accept()

spawn(talk,conn)#原来造线程的方式:

#t = Thread(target=talk, args=(conn,))

#t.start()

if __name__ == '__main__':#server('127.0.0.1',8080)

g=spawn(server,'127.0.0.1',8080)

g.join()-------------------------------------------------------------------------------

#客户端

from socket import *

importos

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8080))whileTrue:

msg='%s say hello' %os.getpid()

client.send(msg.encode('utf-8'))

data=client.recv(1024)print(data.decode('utf-8'))

知识点二:基于网络的IO模型

可以分为2大类:

第一类:

server.accept()

第二类:

conn.recv()

conn.send()

1.recv(收消息)

wait data:等待客户端产生数据--》客户端OS--》网络--》服务端操作系统缓存

copy data:由本地操作系统缓存中的数据拷贝到应用程序的内存中

2.send(发消息)

copy data

阻塞IO:blocking IO

blocking IO就是在执行的2个阶段(等待数据和拷贝数据两个阶段)都被block了

非阻塞IO:non-blocking IO:

思考:目的就是将将recv()阻塞的这个模型变为非阻塞,

即如果没有数据先执行其它的任务,并且最好在没有数据时客户端能返回一个

没有数据的提示信息

非阻塞IO:的问题

1.CPU占用率高(多数的询问是无用的)

2.for循环列表多的情况下会慢

3.数据得不到及时的处理(因为有时候存在可能刚切换到其他任务,数据就过来了

这样就不能及时处理)

非阻塞IO模型改写socket套接字服务端、客户端:

#服务端

from socket import *

importtime

server=socket(AF_INET, SOCK_STREAM)

server.bind(('127.0.0.1',8080))

server.listen(5)

server.setblocking(False)#所有的IO行为都变为非阻塞模型,设置为False

conn_l=[] #保存的是一堆连接,便于后面与每个客户端通信收发消息

whileTrue:#建立连接循环:

try:print('总连接数[%s]' %len(conn_l))

conn,addr=server.accept()

conn_l.append(conn)except BlockingIOError: #客户建立连接请求,报错信息,捕捉这个异常可以执行下面的代码,即执行其他任务

#print('客户端没有数据过来,可以执行其他任务')

del_l=[] #另外新建一个列表单独存放那些非法数据,方便最后清理这些数据

#建立通信循环:

for conn inconn_l:try:

data=conn.recv(1024) #与accept()建立连接一样,如果没有消息过来,直接

if len(data) == 0: #输入的是空值

del_l.append(conn)continueconn.send(data.upper())exceptBlockingIOError:pass

except ConnectionResetError: #客户端单方面终止连接

del_l.append(conn) #将终止的连接添加到定义的列表里面,最后统一删除这些无效的连接

for conn indel_l:

conn_l.remove(conn)

-----------------------------------------------------------------------------------------------------------------

#客户端:不需要变动

from socket import *

import os

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8080))

while True:

msg='%s say hello' %os.getpid()

client.send(msg.encode('utf-8'))

data=client.recv(1024)

print(data.decode('utf-8'))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值