Linux下,能夠經過設置socket使其變爲non-blocking。當對一個non-blocking socket執行讀操做時,流程是這個樣子:python
從圖中能夠看出,當用戶進程發出read操做時,若是kernel中的數據尚未準備好,那麼它並不會block用戶進程,而是馬上返回一個error。從用戶進程角度講 ,它發起一個read操做後,並不須要等待,而是立刻就獲得了一個結果。用戶進程判斷結果是一個error時,它就知道數據尚未準備好,因而用戶就能夠在本次到下次再發起read詢問的時間間隔內作其餘事情,或者直接再次發送read操做。一旦kernel中的數據準備好了,而且又再次收到了用戶進程的system call,那麼它立刻就將數據拷貝到了用戶內存(這一階段仍然是阻塞的),而後返回。app
也就是說非阻塞的recvform系統調用以後,進程並無被阻塞,內核立刻返回給進程,若是數據還沒準備好,此時會返回一個error。進程在返回以後,能夠乾點別的事情,而後再發起recvform系統調用。重複上面的過程,循環往復的進行recvform系統調用。這個過程一般被稱之爲輪詢。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程, 進行數據處理。須要注意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。
socket
因此,在非阻塞式IO中,用戶進程實際上是須要不斷的主動詢問kernel數據準備好了沒有。spa
非阻塞IO示例操作系統
服務端
code
import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(("127.0.0.1", 8080)) server.listen(5) server.setblocking(False) # 默認是True爲阻塞 rlist = [] wlist = [] print("starting.....") while True: try: # 服務端屢次創建鏈接 conn, addr = server.accept() rlist.append(conn) print(rlist) except BlockingIOError: # print("幹其餘活") # 收消息 del_rlist = [] for conn in rlist: try: data = conn.recv(1024) if not data: # 收空 del_rlist.append(conn) continue wlist.append((conn, data.upper())) except BlockingIOError: continue except Exception: conn.close() del_rlist.append(conn) # 發消息 del_wlist = [] for item in wlist: try: conn = item[0] data = item[1] conn.send(data) del_wlist.append(item) except BlockingIOError: pass for item in del_wlist: wlist.remove(item) for conn in del_rlist: rlist.remove(conn) while True: try: data = conn.recv(1024) if not data: break conn.send(data.upper()) except ConnectionRefusedError: break conn.close()
客戶端
orm
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 8080)) while True: msg = input(">>>:").strip() if not msg: continue client.send(msg.encode("utf-8")) data = client.recv(1024) print(data.decode("utf-8"))
可是非阻塞IO模型毫不被推薦。server
咱們不可否則其優勢:可以在等待任務完成的時間裏幹其餘活了(包括提交其餘任務,也就是 「後臺」 能夠有多個任務在「」同時「」執行)。blog
可是也難掩其缺點:接口
1. 循環調用recv()將大幅度推高CPU佔用率; 2. 任務完成的響應延遲增大了,由於每過一段時間纔去輪詢一次read操做,而任務可能在兩次輪詢之間的任意時間完成。 這會致使總體數據吞吐量的下降。
此外,在這個方案中更多的是起到檢測「操做是否完成」的做用,實際操做系統提供了更爲高效的檢測「操做是否完成「做用的接口,例如select()多路複用模式,能夠一次檢測多個鏈接是否活躍。