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