Python Select模型
IO多路复用
IO多路复用就是我们经常说的select epoll.select和epoll的好处是单个process就可以同时处理多个网络IO。基本原理是select\epoll会不断的轮询所负责的所有socket,当有某个socket数据到达了,就通知用户进程。
下面是流程图:
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
注意1:select函数返回结果中如果有文件可读了,那么进程就可以通过调用accept()或recv()来让kernel将位于内核中准备到的数据copy到用户区。
注意2: select的优势在于可以处理多个连接,不适用于单个连接
selectors
基于select模块实现的IO多路复用
`
IO多路复用实现机制
在不同的平台上是不一样的,win平台只有select,Linux平台有select poll epoll
win: select
linux : select poll epoll
通常是用户空间创建fd,然后copy到内核空间
如果是开fd的数量多,select的的效率低
基于select模块实现的IO多路复用
import selectors import socket sock = socket.socket() sock.bind(("127.0.0.1", 8810)) sock.listen(5) # 这里虽然设置了最大连接数,但是已经没有限制了 sel = selectors.DefaultSelector() # 实例化一个对象,会根据不同的平台自动设置优先级 # epoll|kqueue|devpoll > poll > select. 所以Linux系统会自动设置成epoll win 自动设置成select # 第二步 def read(conn, mask): # pass try: # win 检测异常 当有异常 如客户端断开的时候 data = conn.recv(1024) print(data) print(data.decode("utf-8")) data2 = input(">>>") conn.send(data2.encode("utf-8")) except Exception: sel.unregister(conn) # 解除注册 # 第一步 def accept(sock, mask): # mask 是没有用的 conn, addr = sock.accept() # print(conn) sel.register(conn, selectors.EVENT_READ, read) # 把conn 添加到列表中 # 首先要注册 只是把sock和 accept绑定 sel.register(sock, selectors.EVENT_READ, accept) # 注册,但是没有监听accept函数 # 监听 while 1: print("waiting...") # event 就是那个r events = sel.select() # [(key,mask) ,(key,mask) ,(key,mask) ,)] # 活活动的对象会自动添加到这里 for key, mask in events: # events 是个列表 需要遍历 print(key.data) # 拿到accept函数 print(key.fileobj) # 当前的活动的对象 sock 文件句柄 func = key.data # 调用 obj = key.fileobj # func(obj, mask) # 第一个参数是sock 对象 # break
select缺点:
- 每次调用slect都要将所有的fd拷贝到内核空间(每次都要拷贝),导致效率下降
- 监听的的实现是通过遍历所有的fd,(遍历消耗的时间消耗多)判断是否有数据访问
- 最大连接数(input中放的文件描述符数量1024)
poll:
最大连接数没有限制了,除此之外,和select一样,所以基本不用
epoll:
内部通过3个函数实现(select是一个)
第一个函数:
创建epoll句柄,把所有的fd拷贝到内核空间,只需要拷贝一次第二个函数: 回掉函数
某一个函数或者动作成功完成后,会自动触发一个函数
为所有的fd绑定一个回调函数,一旦有数据访问,触发改回调函数,回调函数把fd放到链表中。(只要有活动,把fd放到链表中,动态监听)这样就提高了效率
例子:交试卷第三个函数,判断链表是否为空
selectors.DefaultSelector()
selectors会根据自己的平台选择最佳IO多路复用,自动选择。win只有select
linux的中epoll中的优先级最高
队列queue
和线程有关系的,在多线程的时候有用,保证信息安全的
队列是一种数据类型
优点:
保证线程安全,不用自己加锁
put get
先进先出
import queue q = queue.Queue(3) # 默认是先进先出 FIFO 设置参数是最大的存放数量5 q.put(111) q.put("hello") q.put(222) q.put(333,False) # 默认blocking = True ,False 是当存满的时候,自动报错,解除阻塞的状态 print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 第4次已经拿不到了 取不到 默认阻塞 q.get(False) # 解除阻塞状态
先进后出
q = queue.LifoQueue() q.put(111) q.put(222) print(q.get()) print(q.get())
优先级
q = queue.PriorityQueue() q.put([4,"hello4"]) q.put([1,"hello1"]) q.put([2,"hello2"]) print(q.get()) print(q.get()) print(q.get())
join 与task_done方法
import queue q = queue.Queue(5) q.put(111) q.put(222) q.get() q.task_done() # q.get() q.task_done() # q.join() # 等待task_done 和events是一个原理 print('endnig')
join 与task_done方法必须配合使用
其他的用法
q.qsize() 返回队列的大小 q.empty() 如果队列为空,返回True,反之False q.full() 如果队列满了,返回True,反之False q.full 与 maxsize 大小对应 q.get([block[, timeout]]) 获取队列,timeout等待时间 q.get_nowait() 相当q.get(False)非阻塞 q.put(item) 写入队列,timeout等待时间 q.put_nowait(item) 相当q.put(item, False) q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 q.join() 实际上意味着等到队列为空,再执行别的操作
生产者消费者模型
有生产数据的线程
有消费数据的线程
通过一个容器来解决生产者消费者强耦合的问题
这个容器是用来解耦的(类似吃饭的时候的服务员)
目录结构也是一种解耦
下面是用队列模拟实现
import time,random import queue,threading q = queue.Queue() def Producer(name): count = 0 while count <10: print("making........") time.sleep(random.randrange(3)) q.put(count) print('Producer %s has produced %s baozi..' %(name, count)) count +=1 #q.task_done() #q.join() print("ok......") def Consumer(name): count = 0 while count <10: time.sleep(random.randrange(4)) if not q.empty(): data = q.get() #q.task_done() #q.join() print(data) print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data)) else: print("-----no baozi anymore----") count +=1 p1 = threading.Thread(target=Producer, args=('A',)) c1 = threading.Thread(target=Consumer, args=('B',)) # c2 = threading.Thread(target=Consumer, args=('C',)) # c3 = threading.Thread(target=Consumer, args=('D',)) p1.start() c1.start() # c2.start() # c3.start()
- [Python]Socket高级 -- select I/O复用模型(二)
- python实现select和epoll模型socket网络编程 推荐
- [Python]Socket高级 -- select I/O复用模型(一)
- python socket Select模型
- Python自动化之select、greenlet和gevent和事件驱动模型初探
- 总结网络IO模型与select模型的Python实例讲解
- python 3下基于select模型的事件驱动机制程序
- 状态集合[Python]Socket高级 -- select I/O复用模型(二)
- 总结网络IO模型与select模型的Python实例讲解
- 总结网络IO模型与select模型的Python实例讲解
- Linux下I/O多路复用select, poll, epoll 三种模型的Python使用
- 基于select模型的python echo server
- 参数文件描述符[Python]Socket高级 -- select I/O复用模型(一)
- python实现select和epoll模型socket网络编程
- Python Select模型
- WSAEventSelect模型
- select模型的应用
- Winsock I/O模型之WSAAsyncSelect
- 一个linux典型的select模型更新方法
- Winsock Select模型范例