非阻塞IO

非阻塞IO(non-blocking IO)

Linux下,能夠經過設置socket使其變爲non-blocking。當對一個non-blocking socket執行讀操做時,流程是這個樣子:python

從圖中能夠看出,當用戶進程發出read操做時,若是kernel中的數據尚未準備好,那麼它並不會block用戶進程,而是馬上返回一個error。從用戶進程角度講 ,它發起一個read操做後,並不須要等待,而是立刻就獲得了一個結果。用戶進程判斷結果是一個error時,它就知道數據尚未準備好,因而用戶就能夠在本次到下次再發起read詢問的時間間隔內作其餘事情,或者直接再次發送read操做。一旦kernel中的數據準備好了,而且又再次收到了用戶進程的system call,那麼它立刻就將數據拷貝到了用戶內存(這一階段仍然是阻塞的),而後返回。app

也就是說非阻塞的recvform系統調用調用以後,進程並無被阻塞,內核立刻返回給進程,若是數據還沒準備好,
此時會返回一個error。進程在返回以後,能夠乾點別的事情,而後再發起recvform系統調用。重複上面的過程,
循環往復的進行recvform系統調用。這個過程一般被稱之爲輪詢。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,
進行數據處理。須要注意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。

 

因此,在非阻塞式IO中,用戶進程實際上是須要不斷的主動詢問kernel數據準備好了沒有。socket

非阻塞IO示例

server------------------------

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2018/6/19 9:33
# @File    : server1.py


from socket import *


server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
server.setblocking(False)  # 使得下面全部IO都是非阻塞
print('starting...........')

rlist = []
wlist = []
while True:
    try:
        conn, addr = server.accept()  # 創建鏈接
        rlist.append(conn)
        print(rlist)
    except BlockingIOError:

        # 收消息
        del_rlist = []
        for conn in rlist:
            try:
                data = conn.recv(1024)
                if not data:
                    del_rlist.append(conn)
                    continue
                # con.send(data.upper())  # 這種方式在數據量大的時候也會有問題
                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 del_con in del_rlist:
            rlist.remove(del_con)


server.close()
View Code


# 缺點,一、沒法及時響應客戶端二、cpu無用功一直運行(死循環)ide

client---------------------

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2018/6/19 9:33
# @File    : client1.py
# 右鍵運行一次就是一個進程

from socket import *
client = socket(AF_INET, 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'))

client.close()
View Code

可是非阻塞IO模型毫不被推薦。

咱們不可否則其優勢:可以在等待任務完成的時間裏幹其餘活了(包括提交其餘任務,也就是 「後臺」 能夠有多個任務在「」同時「」執行)。spa

可是也難掩其缺點:操作系統

1. 循環調用recv()將大幅度推高CPU佔用率;這也是咱們在代碼中留一句time.sleep(2)的緣由,不然在低配主機下極容易出現卡機狀況
2. 任務完成的響應延遲增大了,由於每過一段時間纔去輪詢一次read操做,而任務可能在兩次輪詢之間的任意時間完成。
這會致使總體數據吞吐量的下降。

 此外,在這個方案中recv()更多的是起到檢測「操做是否完成」的做用,實際操做系統提供了更爲高效的檢測「操做是否完成「做用的接口,例如select()多路複用模式,能夠一次檢測多個鏈接是否活躍。3d

相關文章
相關標籤/搜索