select 的问题:python
1.当进程被唤醒不清楚到底哪一个socket有数据,只能遍历一遍linux
2.每一次select的执行,都须要将这进程,再加入到等待队列中面试
为了防止重复添加等待队列,当某一次操做完成时,也必须从等待队列中删除进程服务器
因此select最大限制被设置为了1024 ,如此看来select连多线程都比不上多线程
因而推出了poll 和 epoll并发
poll只是简单对select进行了优化,可是还不够完美 ,epoll才是最后的解决方案socket
注意: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模型 多路复用 是最好的解决方案
面试官若是问到高并发,从进程开始介绍