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模型 多路複用 是最好的解決方案
面試官若是問到高併發,從進程開始介紹