阻塞IO(blocking IO)
在linux中,默認情況下所有的socket都是blocking,一個典型的讀操作流程大概是這樣:
當用戶進程調用了recvfrom這個系統調用,kernel內核就開始了IO的第一個階段:準備數據。對于network io( 網絡io )來說,很多時候數據在一開始還沒有到達(比如,還沒有收到一個完整的UDP包),這個時候kernel( 內核 )就要等待足夠的數據到來。
等著對方把數據放到自己操作系統內存
而在用戶進程這邊,整個進程會被阻塞。當kernel一直等到數據準備好了,它就會將數據從kernel操作系統緩存中拷貝到用戶應用程序內存,
然后kernel返回結果,用戶進程才解除block的狀態,重新運行起來。
這就是阻塞IO
所以,blocking IO的特點就是在IO執行的兩個階段(等待數據和拷貝數據兩個階段)都被block了
網絡編程都是從listen\(\)、send\(\)、recv\(\) 等接口開始的,
使用這些接口可以很方便的構建服務器/客戶機的模型。然而大部分的socket接口都是阻塞型的。如下圖ps:
所謂阻塞型接口是指系統調用(一般是IO接口)不返回調用結果并讓當前線程一直阻塞
只有當該系統調用獲得結果或者超時出錯時才返回。
服務端:
from socket import * server = socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8000)) server.listen(5) while True: print("starting...") conn,addr = server.accept() print(addr) while True: try: data = conn.recv(1024) if not data:break conn.send(data.upper()) except ConnectionResetError: break server.close()
客戶端
from socket import * client = socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8000)) 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()
實際上,除非特別指定,幾乎所有的IO接口 ( 包括socket接口 ) 都是阻塞型的。這給網絡編程帶來了一個很大的問題,如在調用recv(1024)的同時,線程將被阻塞,在此期間,線程將無法執行任何運算或響應任何的網絡請求。
一個簡單的解決方案:
在服務器端使用多線程(或多進程)。多線程(或多進程)的目的是讓每個連接都擁有獨立的線程(或進程),
這樣任何一個連接的阻塞都不會影響其他的連接。
該方案的問題是 :
開啟多進程或都線程的方式,在遇到要同時響應成百上千路的連接請求,則無論多線程還是多進程都會嚴重占據系統資源,
降低系統對外界響應效率,而且線程與進程本身也更容易進入假死狀態。
隨著客戶端數量增多,無限制的開線程,開銷非常大
不能解決阻塞IO問題 ,解決思路:起多線程
改進方案:
使用“線程池”或“連接池”?!熬€程池”旨在減少創建和銷毀線程的頻率,
其維持一定合理數量的線程,并讓空閑的線程重新承擔新的執行任務?!斑B接池”維持連接的緩存池,盡量重用已有的連接、
減少創建和關閉連接的頻率。這兩種技術都可以很好的降低系統開銷,都被廣泛應用很多大型系統,如websphere、tomcat和各種數據庫等。
改進后方案其實也存在著問題:
“線程池”和“連接池”技術也只是在一定程度上緩解了頻繁調用IO接口帶來的資源占用。而且,所謂“池”始終是有限,
當請求大大超過上限時,“池”構成的系統對外界的響應并不比沒有池的時候效果好多少。所以使用“池”必須考慮其面臨的響應規模,
并根據響應規模調整“池”的大小。
線程池應該隨著規模數調大,但是調大線程池,要在機器可承受范圍之內。不能把線程池無限調大,這樣相當于無限開線程一樣,
多線程還是要用在規模比較小的情況
對應上例中的所面臨的可能同時出現的上千甚至上萬次的客戶端請求,“線程池”或“連接池”或許可以緩解部分壓力,但是不能解決所有問題??傊嗑€程模型可以方便高效的解決小規模的服務請求,但面對大規模的服務請求,多線程模型也會遇到瓶頸,可以用非阻塞接口來嘗試解決這個問題。
總結:
始綜沒有解決單線程遇到IO問題,單線程遇到IO,就阻塞,用的是阻塞IO模型。
阻塞IO模型就是遇到IO阻塞不處理,就在原地等著。
應該:
監測單線程IO,遇到IO了,這個線程不要阻塞。直接切換到另外一個線程運行,這樣單線程效率就非常高了。
要解決的問題是:
單線程IO問題
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
