python编程之select模块的select异步I/O模型

  在Python中的异步I/O的基础就是select模块的select函数。标准库中的asyncore和asynchat模块对它们进行了进一步的包装,可以从更高层次来处理异步I/O。poll函数和select函数一样,也属于select模块,这两个函数的功能基本一样,相对而言poll的伸缩性更好,但其只能在UNIX、Linux系统上使用使用。
  
  我们想实现一个高效的连接网络服务器程序,可以使用多进程和多线程,来一个连接起一个进程或者线程,这样量大必定低效。好在有了select、poll(epoll)等模型帮我们解决了这个问题。我们知道python中由于GIL的机制,导致线程只能在单核上跑,实际上也是串行运行的,任意时刻只有一个任务在运行,这样如果每个任务中有比如I/O操作的话,由于CPU和硬盘速率的不匹配,势必会造成CPU在等待每个线程的I/O操作,此时产生了I/O阻塞,CPU资源被白白浪费了,实际的结果就是多线程也不一定会比串行的同步程序快多少甚至没啥区别。而异步select模型的出现正好解决了可能产生的阻塞问题,把程序的阻塞降到了最低,此模型中当有阻塞发生时CPU不必等待,可以去执行其他的任务,大大提高了CPU的利用率,降低了程序整体的阻塞时间,这样来看就是程序可能比同步的时候运行的更流畅了(不一定会运行的快),用户体验更好了,同时程序也比使用进程或线程更容易控制和更稳定了。
  
select函数需要3个序列作为它的必选参数(输入、输出、异常情况),第四个参数是可选的,表示以秒为单位的超时时间。

关于模型的详细说明请参考:三种模型--同步、多线程、异步说明

下面是一个使用select的简单示例:

Server端:

#! /usr/bin/env python
# -*- coding:utf-8 -*-
'''
Write by zhulei on 2015年9月17日
Auther: zhulei
'''
import socket,select

#在8008端口上监听
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('0.0.0.0',8008))
server.listen(5)
#存放要被select监控的可以读的对象
inputs = [server]
#存放要被select监控的可以写的对象
outputs = []
while 1:
    rs,ws,es=select.select(inputs,outputs,[])
    #print rs,ws
    #for r in rs:
    #监控客户端的连接,如果有新的连接,接受新的连接并存放到相应的对象列表中
    if server in rs:
        #if r is server:
        clientsock,clientaddr=server.accept()
        inputs.append(clientsock)
        outputs.append(clientsock)
        for i in outputs:
            i.send("欢迎登陆")
    #如果没有新的连接,就检查已经存在的连接有没有数据请求(包含数据传输和退出关闭连接)
    else:
        for i in range(len(rs)):
            data = rs[i].recv(1024)
            #处理关闭连接
            if data == 'q':
                inputs.remove(rs[i])
                outputs.remove(rs[i])
            #处理数据(此处是处理过程,这里只是简单演示)
            else:
                #print data
                #for s in outputs:
                #    if s != rs[i]: 
                #        s.send(data)
                rs[i].send(data)

Client端:

#! /usr/bin/env python
# -*- coding:utf-8 -*-
'''
Write by zhulei on 2015年9月17日
Auther: zhulei
'''
from socket import *
try:
    sock = socket(AF_INET, SOCK_STREAM)
    sock.connect(('127.0.0.1',8008))
except Exception,e:
    print e
    
while 1:    
    data = sock.recv(1024)
    print data    
    p = raw_input("输入内容:").strip()
    if p == 'q':
        sock.send(p)
        sock.close()
    else:        
        sock.send(p)
        #data = sock.recv(1024)
        #print data
用select模型来解决多个客户端的异步连接问题,实现非阻塞的socket连接,提高了CPU利用率,从而提供更好的性能。但这个模型也有其自身的缺点,它需要一个死循环不停的去遍历所有的客户端套接字集合,询问是否有数据到来,这样,如果连接的客户端很多,势必会影响处理客户端请求的效率,但它的优点就是解决了每一个客户端都去开辟新的线程与其通信的开销。

1432433850276575.png


Comments are closed.