Python併發編程(八):IO模型

一、iO模型介紹

爲了更好地瞭解IO模型,我們需要事先回顧下:同步、異步、阻塞、非阻塞

本文討論的背景是Linux環境下的network IO。本文最重要的參考文獻是Richard Stevens的“UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking ”,6.2節“I/O Models ”,Stevens在這節中詳細說明了各種IO的特點和區別,如果英文夠好的話,推薦直接閱讀。Stevens的文風是有名的深入淺出,所以不用擔心看不懂。本文中的流程圖也是截取自參考文獻。

Stevens在文章中一共比較了五種IO Model: blocking IO、nonblocking IO 、IO multiplexing 、signal driven IO 、asynchronous IO 由signal driven IO(信號驅動IO)在實際中並不常用,所以主要介紹其餘四種IO Model。

一、阻塞IO(blocking IO)

在linux中,默認情況下socket網絡編程中的recv、accept等大部分都是blocking,一個典型的讀操作流程大概是這樣:
在這裏插入圖片描述最傳統的一種IO模型,即在讀寫數據過程中會發生阻塞現象。
當用戶發起recvfrom或recv時,內核會去查看數據是否就緒,如果沒有就緒就會等待數據就緒,而用戶線程就會處於阻塞狀態,用戶線程交出CPU。當數據就緒之後,內核會將數據拷貝到用戶線程,並返回結果給用戶線程,用戶線程才解除block狀態。

典型的阻塞IO模型的例子爲:

data = socket.recv();

我們面臨的可能同時出現的上千甚至上萬次的客戶端請求,“線程池”或“連接池”或許可以緩解部分壓力,但是不能解決所有問題。總之,多線程模型可以方便高效的解決小規模的服務請求,但面對大規模的服務請求,多線程模型也會遇到瓶頸,可以用非阻塞接口來嘗試解決這個問題。

二、非阻塞IO(nonblocking IO)

當用戶線程發起一個recv操作後,並不需要等待,而是馬上就得到了一個結果。如果結果是一個error時,它就知道數據還沒有準備好,於是它可以再次發送recv操作。一旦內核中的數據準備好了,並且又再次收到了用戶線程的請求,那麼它馬上就將數據拷貝到了用戶線程,然後返回。

所以事實上,在非阻塞IO模型中,用戶線程需要不斷地詢問內核數據是否就緒,也就說非阻塞IO不會交出CPU,而會一直佔用CPU

在這裏插入圖片描述

三、IO多路複用(IO multiplexing)

多路複用IO模型是目前使用得比較多的模型。Java NIO實際上就是多路複用IO

在多路複用IO模型中,會有一個線程不斷去輪詢多個socket的狀態,只有當socket真正有讀寫事件時,才真正調用實際的IO讀寫操作。因爲在多路複用IO模型中,只需要使用一個線程就可以管理多個socket,系統不需要建立新的進程或者線程,也不必維護這些線程和進程,並且只有在真正有socket讀寫事件進行時,纔會使用IO資源,所以它大大減少了資源佔用

不過要注意的是,多路複用IO模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件逐一進行響應。因此對於多路複用IO模型來說,一旦事件響應體很大,那麼就會導致後續的事件遲遲得不到處理,並且會影響新的事件輪詢,事件太多,輪詢週期太長
在這裏插入圖片描述

四、異步IO(asynchronous IO)

異步IO模型纔是最理想的IO模型,在異步IO模型中,當用戶線程發起recv操作之後,立刻就可以開始去做其它的事。而另一方面,從內核的角度,當它受到一個asynchronous recv之後,它會立刻返回,說明read請求已經成功發起了,因此不會對用戶線程產生任何block。然後,內核會等待數據準備完成,然後將數據拷貝到用戶線程,當這一切都完成之後,內核會給用戶線程發送一個信號,告訴它read操作完成了。也就說用戶線程完全不需要關心實際的整個IO操作是如何進行的,只需要先發起一個請求,當接收內核返回的成功信號時表示IO操作已經完成,可以直接去使用數據了

重點:

  • IO多路複用採用輪詢的方式實現檢測事件的到達,事件太多,輪詢週期太長,造成效率低下
  • 異步IO採用異步回調的機制實現檢測事件的到達,數據到達,立馬觸發異步回調機制,主動告訴內核,數據準備完成,然後內核將數據拷貝到用戶線程,告訴用戶線程recv完畢!
  • 異步IO是需要操作系統的底層支持,在Java 7中,提供了Asynchronous IO。簡稱AIO

在這裏插入圖片描述
總結:前面四種IO模型實際上都屬於同步IO,只有最後一種是真正的異步IO,因爲無論是多路複用IO還是信號驅動模型,IO操作的第2個階段都會引起用戶線程阻塞,也就是內核等待數據的準備,然後進行數據拷貝的過程都會讓用戶線程阻塞。

有人可能想問非阻塞IO不是也不用等待嗎,它爲什麼不是異步的?

1、首先高清楚同步、異步、阻塞、非阻塞的關係。'同步、異步區別是:是否原地等待數據的返回,阻塞和非阻塞區別是:是否會有程序的停頓!'
2、非阻塞是說明在recv處不會停在這裏。'異步不僅僅是不停在哪裏,而且運行recv過後就不管了',有點類似於後臺自己運行。只等着結果就行了。非阻塞還需要try捕捉異常操作。所以它不是異步的!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章