select、poll、epoll三者的区别 

select 

select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组(在linux中一切事物皆文件,块设备,socket连接等。),当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位(变成ready),使得进程可以获得这些文件描述符从而进行后续的读写操作(select会不断监视网络接口的某个目录下有多少文件描述符变成ready状态【在网络接口中,过来一个连接就会建立一个'文件'】,变成ready状态后,select就可以操作这个文件描述符了)。

【socketserver是通过多线程来处理多个请求,每个连接过来分配一个线程来处理,但是select是单进程的,一个进程执行代码肯定就是串行的,但是现在就要通过一个进程来实现并发的效果,一个进程下只有一个主线程,也就说说用一个线程实现并发的效果。为什么要用一个进程实现多并发而不采用多线程实现多并发呢?

==========答:因为一个进程实现多并发比多线程是实现多并发的效率还要高,因为启动多线程会有很多的开销,而且CPU要不断的检查每个线程的状态,确定哪个线程是否可以执行。这个对系统来说也是有压力的,用单进程的话就可以避免这种开销和给系统带来的压力,

那么单进程是如何实现多并发的呢???

========答:很巧妙的使用了生产者和消费者的模式(异步),生产者和消费者可以实现非阻塞,一个socketserver通过select接收多个连接过来(之前的socket一个进程只能接收一个连接,当接收新的连接的时候产生阻塞,因为这个socket进程要先和客户端进行通信,二者是彼此互相等待的【客户端发一条消息,服务端收到,客户端等着返回....服务端等着接收.........】一直在阻塞着,这个时候如果再来一个连接,要等之前的那个连接断了,这个才可以连进来。-----------也就是说用基本的socket实现多进程是阻塞的。为了解决这个问题采用每来一个连接产生一个线程,是不阻塞了,但是当线程数量过多的时候,对于cpu来说开销和压力是比较大的。)对于单个socket来说,阻塞的时候大部分的时候都是在等待IO操作(网络操作也属于IO操作)。为了避免这种情况,就出现了异步=============客户端发起一个连接,会在服务端注册一个文件句柄,服务端会不断轮询这些文件句柄的列表,主进程和客户端建立连接而没有启动线程,这个时候主进程和客户端进行交互,其他的客户端是无法连接主进程的,为了实现主进程既能和已连接的客户端收发消息,又能和新的客户端建立连接,就把轮询变的非常快(死循环)去刷客户端连接进来的文件句柄的列表,只要客户端发消息了,服务端读取了消息之后,有另一个列表去接收给客户端返回的消息,也不断的去刷这个列表,刷出来后返回给客户端,这样和客户端的这次通信就完成了,但是跟客户端的连接还没有断,但是就进入了下一次的轮询。】

  

select 优点

select目前几乎在所有的平台上支持,良好跨平台性。

 

select 缺点

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多的时候会很大
  • 单个进程能够监视的fd数量存在最大限制,在linux上默认为1024(可以通过修改宏定义或者重新编译内核的方式提升这个限制)
  • 并且由于select的fd是放在数组中,并且每次都要线性遍历整个数组,当fd很多的时候,开销也很大

python  select 

调用select的函数为readable,writable,exceptional = select.select(rlist, wlist, xlist[, timeout]),前三个参数都分别是三个列表,数组中的对象均为waitable object:均是整数的文件描述符(file descriptor)或者一个拥有返回文件描述符方法fileno()的对象;

  • rlist: 等待读就绪的list
  • wlist: 等待写就绪的list
  • errlist: 等待“异常”的list

 

select方法用来监视文件描述符,如果文件描述符发生变化,则获取该描述符。
1、这三个list可以是一个空的list,但是接收3个空的list是依赖于系统的(在Linux上是可以接受的,但是在window上是不可以的)。
2、当 rlist 序列中的描述符发生可读时(accetp和read),则获取发生变化的描述符并添加到 readable 序列中
3、当 wlist 序列中含有描述符时,则将该序列中所有的描述符添加到 writable 序列中
4、当 errlist序列中的句柄发生错误时,则将该发生错误的句柄添加到 exceptional 序列中
5、当 超时时间 未设置,则select会一直阻塞,直到监听的描述符发生变化
   当 超时时间 = 时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的描述符(fd)有变化,则直接执行。
6、在list中可以接受Ptython的的file对象(比如sys.stdin,或者会被open()os.open()返回的object),socket object将会返回socket.socket()。也可以自定义类,只要有一个合适的fileno()的方法(需要真实返回一个文件描述符,而不是一个随机的整数)。
 

select 示例:

Python的select()方法直接调用操作系统的IO接口,它监控sockets,open files, and pipes(所有带fileno()方法的文件句柄)何时变成readable 和writeable, 或者通信错误,select()使得同时监控多个连接变的简单,并且这比写一个长循环来等待和监控多客户端连接要高效,因为select直接通过操作系统提供的C的网络接口进行操作,而不是通过Python的解释器

 1 #coding:UTF8
 2 
 3 import select
 4 import socket
 5 import sys
 6 import Queue
 7 
 8 #创建一个TCP/IP 进程
 9 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
10 server.setblocking(0)
11 
12 #连接地址和端口
13 server_address = ('localhost',10000)
14 print >>sys.stderr,'starting up on %s prot %s' % server_address
15 server.bind(server_address)
16 
17 #最大允许链接数
18 server.listen(5)
19 
20 inputs = [ server ]
21 outputs = []
22 
23 message_queues = {}
24 
25 while inputs:
26     print >>sys.stderr,'\nwaiting for the next event'
27     readable,writable,exceptional = select.select(inputs,outputs,inputs)
28 
29     # Handle inputs
30     for s in readable:
31      
32         if s is server:
33             # A "readable" server socket is ready to accept a connection
34             connection, client_address = s.accept()
35             print >>sys.stderr, 'new connection from', client_address
36             #connection.setblocking(0)
37             inputs.append(connection)
38      
39             # Give the connection a queue for data we want to send
40             message_queues[connection] = Queue.Queue()
41         
42         else:
43             data = s.recv(1024)
44             if data:
45                 # A readable client socket has data
46                 print >>sys.stderr, 'received "%s" from %s' % (data, s.getpeername())
47                 message_queues[s].put(data)  #这个s相当于connection
48                 # Add output channel for response
49                 if s not in outputs:
50                     outputs.append(s)
51 
52             else:
53                 # Interpret empty result as closed connection
54                 print >>sys.stderr, 'closing', client_address, 'after reading no data'
55                 # Stop listening for input on the connection
56                 if s in outputs:
57                     outputs.remove(s)  #既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
58                 inputs.remove(s)    #inputs中也删除掉
59                 s.close()           #把这个连接关闭掉
60                  
61                 # Remove message queue
62                 del message_queues[s]
63     
64     # Handle outputs
65     for s in writable:
66         try:
67             next_msg = message_queues[s].get_nowait()
68         except Queue.Empty:
69             # No messages waiting so stop checking for writability.
70             print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'
71             outputs.remove(s)
72         else:
73             print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())
74             s.send(next_msg.upper())
75     # Handle "exceptional conditions"
76     for s in exceptional:
77         print >>sys.stderr, 'handling exceptional condition for', s.getpeername()
78         # Stop listening for input on the connection
79         inputs.remove(s)
80         if s in outputs:
81             outputs.remove(s)
82         s.close()
83      
84         # Remove message queue
85         del message_queues[s]     
server

 

相关文章:

  • 2021-07-03
  • 2022-01-09
  • 2021-09-12
  • 2021-09-28
  • 2021-09-10
猜你喜欢
  • 2021-04-27
  • 2021-12-24
  • 2022-12-23
  • 2021-06-29
  • 2022-01-29
  • 2021-11-29
相关资源
相似解决方案