Python异步编程原理篇之IO多路复用模块selector
1|0selector 简介
selector 是一个实现了IO复用模型的python包,实现了IO多路复用模型的 select、poll 和 epoll 等函数。
它允许程序同时监听多个文件描述符(例如套接字),并在其中任何一个就绪时进行相应的操作。这样可以有效地管理并发 I/O 操作,提高程序的性能和资源利用率。
本篇主要讲解selector编程示例,以socket编程为主题,首先分析阻塞IO模型的网络编程,然后对比selector实现的IO多路复用模型的网络编程。
2|0阻塞IO模型下的 socket 网络编程
通过socket实现最简单的客户端和服务端通信的功能,阻塞IO模型的特点就是在文件IO或网络IO时获取数据的函数会一直阻塞,直到数据到来。
服务端:server.py
客户端:client.py
启动服务端,未启动客户端
启动服务端,代码执行到client_socket, client_address = server_socket.accept()
暂停,accept 是一个阻塞函数。
函数说明:
client_socket, client_address = server_socket.accept()
阻塞接口,等待客户端的连接,没有客户端连接时阻塞等待,有客户端连接时返回新的聊天socket,用于后续发送和接收消息
继续启动客户端
启动客户端之后,客户端连接服务端,服务端代码执行到data = client_socket.recv(1024).decode()
, recv 是一个阻塞函数。
函数说明:
data = client_socket.recv(1024).decode()
阻塞接口,等待缓冲区有消息到来。没有消息时阻塞等待,有消息到来返回消息内容
客户端发送消息
客户端发送消息,data = client_socket.recv(1024).decode()
收到消息,从网络协议栈中获取消息,并返回。继续whie True 循环的下一轮循环,阻塞在相同地方。
3|0IO多路复用模型下的socket 网络编程 selector
selector是实现IO多路复用模型的模块,首先回忆一下IO多路复用。
IO多路复用是通过select
、poll
、epoll
监听文件句柄,当有文件句柄处于就绪状态,就通知对应的应用程序处理。
服务端:server.py
主要的函数:
一、自动选择文件IO模型selectors.DefaultSelector()
选择一个当前平台最优的IO模型,一般来说是epoll或kqueue。存在的可选项包括:
- SelectSelector
- PollSelector
- EpollSelector
- DevpollSelector
- KqueueSelector
DefaultSelector 是一个指向当前平台上可用的最高效实现的别名,当选择epoll时,可以认为 sel = EpollSelector
。
返回:一个select对象
二、文件注册 sel.register(sock, selectors.EVENT_READ, accept)
函数原型:
注册一个用于选择的文件对象,在其上监视 I/O 事件。
fileobj 是要监视的文件对象。 它可以是整数形式的文件描述符或者具有 fileno() 方法的对象。
events 是要监视的事件的位掩码。
data 是一个任意对象或变量。
返回:
这将返回一个新的 SelectorKey 实例,实例具体内容见下一个函数的key
三、获取就绪文件events = sel.select()
函数原型:
可用于遍历获取状态变为就绪注册的文件,如果设置超时时间则可能会抛出超时异常。
返回:一个(key, events)的元组,
-
key: 一个SelectorKey类的实例,包括
-
events:文件句柄可读还是可写的标识。为EVENT_READ或EVENT_WRITE,或者二者的组合
client.py
服务端启动,代码完成的功能包括:
- 创建一个socket,并绑定IP,监听端口
- 设置socket为非阻塞,否则超时会报错
- 将socket注册到 selector 中,等待socket就绪,绑定就绪之后的回调函数accept
- 进入while True循环,访问select返回的就绪列表。这个阻塞函数,没有文件读写就绪就会阻塞。
启动一个客户端,客户端连接到服务端,socket文件句柄有连接请求,select返回可读状态的socket。返回的events是一个列表,当中只有一个就绪的文件句柄。
key.data拿到注册的回调函数也就是accept函数,key.fileobj拿到文件句柄的socket对象。调用accept函数,传入socket对象。
accept中先通过accept接收连接,返回通信使用的文件句柄conn,然后设置conn为非阻塞,最后将conn阻塞到selector中,传入回调函数read。等conn文件句柄可读时,就表示有数据发送过来,就可以调用read函数读取内容了。
客户端发送消息
客户端发送消息时,selector会返回可读状态的conn文件句柄,从返回对象中获取回调函数,调用回调函数read,传入文件句柄。
在read函数中,首先获取了网络协议栈中的消息内容,然后判断消息是否为关闭连接。如果不是则发送一条消息给对方。
整个基于IO多路复用模型的网络编程流程就是这样。
4|0selector 原理分析
selector是操作系统的IO多路复用模型的一种实现。通过select
、poll
、epoll
监听文件句柄,在文件句柄可读的状态下,会返回就绪的文件句柄。
4|1返回就绪状态文件句柄
循环中访问sel.select()
就是监听文件句柄状态的函数,一个阻塞函数。应用程序调用该函数后会等待,直到有数据到来,数据从设备发送到内核空间,在socket编程中就是数据流从网卡到内核空间中。当数据到达内核空间中,该函数返回文件句柄相关的内容。
4|2数据拷贝
在selector中相关的函数是
conn, addr = server_socket.accept()
data = conn.recv(1024).decode('utf-8')
4|3总结
一个完整的IO多路复用模型就是由两个部分组成,分别是
- 返回就绪状态文件句柄
- 数据拷贝
5|0asyncio 和 selector 的关系
selectors 则是 asyncio 的底层实现之一。asyncio实现的协程是由事件循环
+ 任务
组成的,而selector就是事件循环的重要依赖模块。
asyncio 使用了 selectors 模块来实现底层的并发 I/O 操作。通过将 selectors 的功能封装为 asyncio 提供的事件循环(Event Loop)和其他协程相关的工具。
回顾一下事件循环的机制
事件循环就是一个while True的循环,循环中做的事情有三个:
- 获取就绪状态的任务和已完成的任务
- 执行就绪状态的任务
- 移除已完成的任务
那么selector的功能在事件循环中的功能就非常明显了,就是负责返回IO相关的就绪任务。
asyncio 库使用了底层的 selectors 模块来监听和管理文件描述符的状态变化,并在合适的时候将控制权交给其他的协程。这样可以实现非阻塞的 I/O 操作,并支持高并发和并行执行。
selectors 提供了底层的 I/O 多路复用机制,而 asyncio 在其之上提供了更高级的异步编程框架。
6|0附录asyncio模块事件循环核心模块
连载一系列关于python异步编程的文章。包括同异步框架性能对比、异步事情驱动原理等。欢迎关注微信公众号第一时间接收推送的文章。
__EOF__

本文链接:https://www.cnblogs.com/goldsunshine/p/17976575.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
2022-02-19 Flask中本地代理的使用