Python select模块 I/O多路复用

来源:互联网 发布:mysql 建表时创建索引 编辑:程序博客网 时间:2024/06/07 06:26

Python select模块 I/O多路复用

Python 在 select 模块中提供了异步 I/O(Asynchronous I/O),与 Linux 下的 select 机制相似,但进行一些简化

select 模块

select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组,
每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,
调用select()时,由内核根据IO状态修改fd_set的内容,
由此来通知执行了select()的进程哪一Socket或文件可读或可写。主要用于Socket通信当中。

select怎么调用

并且关注了几个描述字–>系统通知我哪个描述符(某个socket)可读或者可写的时候,它可以给你一个通知,去调用select()

select调用后发生什么

调用了一个 select 函数,select 函数就返回了,告诉我们套接口已经可读,
然后我们去调用read()读这个套接口,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功
可以用阻塞的read或者非阻塞的 read,阻塞 read 是无数据可读就阻塞进程,非阻塞 read是无数据可读就返回一个 EWOULDBLOCK 错误。

非阻塞式I/O编程特点

  1. 如果一个发现I/O有输入,读取的过程中,另外一个也有了输入,这时候不会产生任何反应.这就需要你的程序语句去用到select函数的时候才知道有数据输入。
  2. 程序去select的时候,如果没有数据输入,程序会一直等待,直到有数据为止,也就是程序中无需循环和sleep。

阻塞方式block

顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回
诸如connect、accept、recv或recvfrom这样的阻塞程序

非阻塞方式non-block

就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高

总结:select主要用于socket通信当中,能监视我们需要的文件描述变化。

I/O多路复用技术

某个socket可读或者可写的时候,它可以给你一个通知。这样当配合非阻塞的socket使用时,只有当系统通知我哪个描述符可读了,我才去执行read操作,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功
操作系统的这个功能通过select/poll/epoll/kqueue之类的系统调用函数来使用,这些函数都可以同时监视多个描述符的读写就绪状况,这样,多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这就叫I/O多路复用,这里的“复用”指的是复用同一个线程。
举个例子子

作者:郭春阳
链接:https://www.zhihu.com/question/28594409/answer/52835876
来源:知乎
模拟一个tcp服务器处理30个客户socket。假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:
1. 第一种选择:按顺序逐个检查,先检查A,然后是B,之后是C、D。。。这中间如果有一个学生卡主,全班都会被耽误。这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。
2. 第二种选择:你创建30个分身,每个分身检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者线程处理连接。
3. 第三种选择,你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。。。
这种就是IO复用模型,Linux下的select、poll和epoll就是干这个的。
将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。这样,整个过程只在调用select、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor模式。

多线程来并行处理多路 socket I/O

select 方法允许你响应不同 socket 的多个事件以及其它不同事件。

例如:
你可以让 select在某个 socket 有数据到达时,或者当某个 socket 可以写数据时,又或者是当某个 socket 发生错误时通知你,
好处是你可以同时响应很多 socket 的多个事件
Linux 下 C 语言的 select 使用到 位图来表示我们要关注哪些文件描述符的事件
python 中使用 list 来表示我们监控的文件描述符
当有事件到达时,返回的也是文件描述符的 list,表示这些文件有事件到达

表示等待从标准输入中获得输入

rlist,wlist,elist = select.select([sys.stdin],[],[])print sys.stdin.read()

参数:

可接受四个参数(前三个必须)
select 方法的前三个参数都是 list 类型,
分别代表读事件写事件错误事件,timeout 超过时间

返回值:

同样方法返回值也是三个 list
包含的是哪些事件(读、写、异常)满足了。
上面的例子,参数只有一个事件 sys.stdin–>表示只关心标准输入事件
select 返回时 rlist 只会是 [sys.stdin]–>表示可以从 stdin 中读入数据了
我们使用 read 方法来读入数据。

select 对于 socket 描述符也是有效的

创建了两个 socket 客户端连接到远程服务器,

select –>用来监控哪个 socket 有数据到达:

import socketimport selectsocket1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)socket2 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)socket1.connect(('192.168.1.1',25))socket2.connect(('192.168.1.1',25))while 1:    #等待读一个事件,参数是,返回的是读到的事件    rlist,wlist,elist = select.select([socket1,scoket2],[],[],5)    #    if [rlist,wlist,elist ] == [[],[],[]]:        print "five seconds elapsed\n"    else:        for sock in rlist:            print sock.recv(100)
原创粉丝点击