select 的问题:
1.当进程被唤醒不清楚到底哪个socket有数据,只能遍历一遍
2.每一次select的执行,都需要将这进程,再加入到等待队列中
为了防止重复添加等待队列,当某一次操作完成时,也必须从等待队列中删除进程
所以select最大限制被设置为了1024 ,如此看来select连多线程都比不上
于是推出了poll 和 epoll
poll只是简单对select进行了优化,但是还不够完美 ,epoll才是最后的解决方案
注意:epoll仅能在linux中使用
案例:
import socket import select s = socket.socket() s.bind(("127.0.0.1",1689)) s.listen() # 创建一个epoll对象 epoll = select.epoll() # 注册读就绪事件 (有数据可以读取了) # s.fileno()用于获取文件描述符 epoll.register(s.fileno(),select.EPOLLIN) # 存储文件描述符与socket的对应关系 fd_sockets = {s.fileno():s} while True: # 该函数是阻塞会直到你关注的事件发生 # 返回值为文件描述符与发生的事件类型 是一个列表 列表中是元组 第一个是描述符 第二个是事件 for fd,event in epoll.poll(): print("有socket 搞事情了!") sock = fd_sockets[fd] # 取出对应的socket对象 # 判断socket是服务器还是客户端 if sock == s: # 执行对应的接收或发送 client,addr = sock.accept() # 注册客户端的事件 epoll.register(client.fileno(),select.EPOLLIN) # 将对应关系存储到字典中 fd_sockets[client.fileno()] = client print("来了一个客户端....") elif event == select.EPOLLIN: #客户端的处理 data = sock.recv(1024) if not data: epoll.unregister(fd) # 注销事件 fd_sockets.pop(fd) # 从字典中删除 sock.close() # 关闭socket continue print("%s 发来问候:%s" % (sock,data.decode("utf-8"))) #将事件转换为可写 epoll.modify(fd,select.EPOLLOUT) else: sock.send("我是服务器 你丫是谁?".encode("utf-8")) # 将事件转换为可读 epoll.modify(fd, select.EPOLLIN)
epoll 如何解决select的两个问题
1.epoll 把对于等待队列的操作 与阻塞进程分开了
2.epoll 自己维护了一个等待队列 避免了遍历所有socket
并发:
多进程 开销大
多线程 开销小于进程 但是不能无限开
协程 避免线程数量达到上线的问题 本质上属于非阻塞IO模型
IO模型 多路复用 是最好的解决方案
面试官如果问到高并发,从进程开始介绍
来源:https://www.cnblogs.com/bladecheng/p/11164536.html