服務端和應用端直接的交互,都會有兩個過程。
1,等待數據准備
即使開多進程或者多線程並沒有解決IO阻塞問題,
server.setblocking(False)
socket模塊可以通過setblocking來修改為非阻塞狀態
當用戶線程發起一個read操作后,並不需要等待,而是馬上就得到了一個結果。如果結果是一個error時,它就知道數據還沒有准備好,於是它可以再次發送recv操作。一旦內核中的數據准備好了,並且又再次收到了用戶線程的請求,那么它馬上就將數據拷貝到了用戶線程,然后返回。所以事實上,在非阻塞IO模型中,用戶線程需要不斷地詢問內核數據是否就緒,也就說非阻塞IO不會交出CPU,而會一直占用CPU。
但是對於非阻塞IO就有一個非常嚴重的問題,在while循環中需要不斷地去詢問內核數據是否就緒,這樣會導致CPU占用率非常高,因此一般情況下很少使用while循環這種方式來讀取數據。
另外多路復用IO為何比非阻塞IO模型的效率高是因為在非阻塞IO中,不斷地詢問socket狀態時通過用戶線程去進行的,而在多路復用IO中,輪詢每個socket狀態是內核在進行的,這個效率要比用戶線程要高的多。
python中用select模塊可以使用這個監管機制
因此,在單個socket時,效率可能不如阻塞IO,但是當監管很多個socket的時候,大大減少資源占用
監管機制:
select機制: windows 和linux機制
poll機制: 只在linux有,poll比select監管的數量更多
epoll機制:linux中存在,slect和poll會出現延遲響應的問題,給每個監管對象都綁定一個回調機制,一有響應,回調機制立刻發起提醒
selectors模塊可以自動根據平台的不同使用不同的監管平台
效率最高,使用最為廣泛
模塊:asyncio模塊
框架:sanic tornado twisted
異步IO模型才是最理想的IO模型,在異步IO模型中,當用戶線程發起read操作之后,立刻就可以開始去做其它的事。而另一方面,從內核的角度,當它受到一個asynchronous read之后,它會立刻返回,說明read請求已經成功發起了,因此不會對用戶線程產生任何block。然后,內核會等待數據准備完成,然后將數據拷貝到用戶線程,當這一切都完成之后,內核會給用戶線程發送一個信號,告訴它read操作完成了。也就說用戶線程完全不需要關心實際的整個IO操作是如何進行的,只需要先發起一個請求,當接收內核返回的成功信號時表示IO操作已經完成,可以直接去使用數據了。
也就說在異步IO模型中,IO操作的兩個階段都不會阻塞用戶線程,這兩個階段都是由內核自動完成,然后發送一個信號告知用戶線程操作已完成。用戶線程中不需要再次調用IO函數進行具體的讀寫。這點是和信號驅動模型有所不同的,在信號驅動模型中,當用戶線程接收到信號表示數據已經就緒,然后需要用戶線程調用IO函數進行實際的讀寫操作;而在異步IO模型中,收到信號表示IO操作已經完成,不需要再在用戶線程中調用iO函數進行實際的讀寫操作。