* 阻塞: 进程由执行态进入阻塞态


a = input() print(a)


from socket import * s = socket() s.bind(('0.0.0.0', 8888)) s.listen(5) print('Listen the port', 8888) c, addr = s.accept() print("Connect from", addr)


from socket import * s = socket() s.bind(('0.0.0.0', 8888)) s.listen(5) print('Listen the port', 8888) c, addr = s.accept() print("Connect from", addr) data = c.recv(1024) print(data)
非阻塞I/O


""" block_io.py socket 非阻塞IO示例 """ from socket import * from time import * # 日志文件 f = open('log.txt','a+') # tcp 服务端 sockfd = socket() sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sockfd.bind(('0.0.0.0',8888)) sockfd.listen(5) # 非阻塞设置 # sockfd.setblocking(False) # 超时时间 sockfd.settimeout(2) while True: print("Waiting from connect...") try: connfd,addr = sockfd.accept() except (BlockingIOError, timeout) as e: sleep(2) f.write("%s : %s\n"%(ctime(),e)) f.flush() else: print("Connect from",addr) data = connfd.recv(1024).decode() print(data)
I/O多路复用


from socket import * from select import select s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('0.0.0.0',8888)) s.listen(5) rlist = [s] wlist = [] xlist = [] while True: rs,ws,xs = select(rlist,wlist,xlist) # rs列表 --> 里面是rlist中准备就绪的I/O for r in rs: print(type(r)) if r is s: c,addr = r.accept() print("Connect from",addr) rlist.append(c) else: # 表明有客户端发送消息 data = r.recv(1024).decode() print(data) r.send(b'OK')


from socket import * from select import * # 创建监听套接字,作为关注的IO s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('0.0.0.0',8888)) s.listen(3) # 创建poll对象 p = poll() # 建立查找字典,通过IO的fileno查找io对象 # 始终与register的IO保持一直 fdmap = {s.fileno(): s} # 关注 s p.register(s,POLLIN|POLLERR) # 循环监控IO发生 while True: events = p.poll() # 阻塞等待IO发生 # 循环遍历查看哪个IO准备就绪 for fd,event in events: print(events) if fd == s.fileno(): c,addr = fdmap[fd].accept() print("Connect from",addr) # 关注客户端连接套接字 p.register(c,POLLIN|POLLHUP) fdmap[c.fileno()] = c # 维护字典 elif event & POLLIN: data = fdmap[fd].recv(1024).decode() if not data: p.unregister(fd) # 取消监控 fdmap[fd].close() del fdmap[fd] # 从字典删除 continue print(data) p.register(fdmap[fd],POLLOUT) elif event & POLLOUT: fdmap[fd].send(b'OK') p.register(fdmap[fd], POLLIN) # 每注册一次都会覆盖上次注册时选定的事件类型


# 创建epoll对象 ep = epoll() # 建立查找字典,通过IO的fileno查找io对象 # 始终与register的IO保持一致 fdmap = {s.fileno():s} # 关注 s ep.register(s,EPOLLIN|EPOLLERR)
3. 三种方案的比较
| select | poll | epoll | |
| 适用的操作系统 | Linux, Unix | Linux | |
| 效率 | / | 低 | 高 |
| 监控I/O数量 | / | 少 | 多 |
| 触发方式 | / | 少 | 多 |
异步I/O
协程
4. Python对协程的支持


import asyncio # 协程IO async def fun1(): aaaaaaaaaa await asyncio.sleep(3) bbbbbbbbbb async def fun2(): cccccccccc await ddddddddddd
* 协程函数的定义需要使用关键字 --> async def func()
* 只有当满足await条件时,才会从fun1中跳出,执行fun2的内容
* 关于await条件 --> 只支持asyncio库中的内容(如asyncio.sleep()), 而不能支持time.sleep(), accept(), recv()等
* 当我们使用常用的模块,而不是asyncio模块时,无法实现跳转,也就实现不了协程
5. 第三方协程模块


from greenlet import greenlet def fun1(): print("执行 fun1") gr2.switch() print("结束 fun1") gr2.switch() def fun2(): print("执行 fun2") gr1.switch() print("结束 fun2") # 将函数变为协程 gr1 = greenlet(fun1) gr2 = greenlet(fun2) gr1.switch() # 选择执行的协程函数
* gevent模块
monkey脚本


import gevent from gevent import monkey monkey.patch_time() # 修改对time模块中阻塞的解释行为 from time import sleep # 协程函数 def foo(a,b): print("Running foo ...",a,b) # gevent.sleep(3) sleep(3) print("Foo again..") def bar(): print("Running bar ...") # gevent.sleep(2) sleep(2) print("Bar again..") # 生成协程对象 f = gevent.spawn(foo,1,2) g = gevent.spawn(bar) gevent.joinall([f,g]) #阻塞等待f,g代表的协程执行完毕
协程是单线程, 生成了协程对象后, 只有当线程的后续执行中遇到了gevent阻塞, 协程函数才会执行, 否则协程函数永远不会执行.
gevent遇到阻塞后, 哪个协程函数可以执行就执行哪个协程函数; 上面的代码中, foo函数是先创建的协程函数,遇到joinall阻塞后,先执行的是foo函数.


import gevent from gevent import monkey monkey.patch_all() # 执行脚本,修改socket from socket import * def handle(c): while True: data = c.recv(1024).decode() if not data: break print(data) c.send(b'OK') c.close() # 创建tcp套接字 s = socket() s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(('0.0.0.0',8888)) s.listen(5) # 循环接收来自客户端连接 while True: c,addr = s.accept() print("Connect from",addr) # handle(c) # 处理具体客户端请求 gevent.spawn(handle,c) # 协程方案
本文深入探讨了阻塞I/O、非阻塞I/O、I/O多路复用及异步I/O的概念与实现,对比了select、poll、epoll的优劣,并介绍了Python协程的原理与应用。

被折叠的 条评论
为什么被折叠?



