1. 程式人生 > >關於執行緒阻塞的問題,留著看了

關於執行緒阻塞的問題,留著看了

 執行緒在執行中如果遇到(I/O 操作)如磁碟讀寫或網路通訊,通常要耗費較長的時間,這時作業系統會剝奪這個執行緒的 CPU 控制權,使其暫停執行,同時將資源讓給其他的工作執行緒,這種執行緒排程方式稱為 阻塞。當 I/O 操作完畢時,作業系統將這個執行緒的阻塞狀態解除,恢復其對CPU的控制權,令其繼續執行。這種 I/O 模式就是通常的同步式 I/O(Synchronous I/O)或阻塞式 I/O(Blocking I/O)。

非阻塞

    非阻塞是這樣定義的,當執行緒遇到 I/O 操作時,不會以阻塞的方式等待 I/O 操作的完成或資料的返回,而只是將 I/O 請求傳送給作業系統,繼續執行下一條語句。當作業系統完成 I/O 操作時,以事件的形式通知執行 I/O 操作的執行緒,執行緒會在特定時候處理這個事件。

對比阻塞與非阻塞

    阻塞模式下,一個執行緒只能處理一項任務,要想提高吞吐量必須通過多執行緒。

    非阻塞模式下,一個執行緒永遠在執行計算操作,這個執行緒所使用的 CPU 核心利用率永遠是 100%,I/O 以事件的方式通知。

    在阻塞模式下,多執行緒往往能提高系統吞吐量,因為一個執行緒阻塞時還有其他執行緒在工作,多執行緒可以讓 CPU 資源不被阻塞中的執行緒浪費。

    而在非阻塞模式下,執行緒不會被 I/O 阻塞,永遠在利用 CPU。多執行緒帶來的好處僅僅是在多核 CPU 的情況下利用更多的核。

同步和非同步、阻塞和非阻塞這是兩組概念,說的是不同的事情,同步和阻塞沒有必然的聯絡,非同步和非阻塞也沒有必然的聯絡。同步和非同步是隻跟IO操作過程中程序的狀態變化有關。阻塞和非阻塞就是程序的兩種狀態。比如你去銀行,排除的話就是一種同步的方式,叫號的話就是非同步的方式。排隊必須自己看著什麼時候輪到自己,而叫號則不必,輪到你的時候會觸發一個事件,或者說你會收到一個訊號,別人會叫你。不管是排除還是叫號,如果你在等待的過程中不能做其他事情,那就是阻塞模式,否則就是非阻塞模式。同步的時候可以有阻塞和非阻塞,非同步的時候也可以有阻塞和非阻塞。

阻塞 I/O

Linux下的I/O操作預設是阻塞I/O,即open和socket建立的I/O都是阻塞I/O。當讀寫操作沒有完成時,函式就不會返回,程序一直阻塞在那裡。

非阻塞I/O    

非阻塞模式的使用並不普遍,因為非阻塞模式會浪費大量的CPU資源。

網路程式設計時listen,recvfrom,connect都會引起阻塞。非阻塞IO通常應用於網路程式設計中,IO請求時加上O_NONBLOCK一類的標誌位,函式立刻返回,IO沒有就緒會返回錯誤,需要請求程序主動輪詢不斷髮IO請求直到返回正確
比如呼叫recvfrom時,如果系統還沒有接收到網路資料,核心馬上返回一個 EWOULDBLOCK的錯誤。

 當一個應用程式使用了非阻塞模式的套接字,它需要使用一個迴圈來不停地測試是否一個檔案描述符有資料可讀(稱做 polling(輪詢))。應用程式不停的 polling 核心來檢查是否 I/O操作已經就緒。這將是一個極浪費 CPU資源的操作。這種模式使用中不是很普遍。

I/O多路複用

針對批量IP操作時,使用I/O多路複用,非常有好。

在使用 I/O 多路技術的時候,我們呼叫 select()函式和 poll()函式或epoll函式(2.6核心開始支援),在呼叫它們的時候阻塞。 當我們呼叫 select函式阻塞的時候,select 函式等待資料報套接字進入讀就緒狀態。當select函式返回的時候, 也就是套接字可以讀取資料的時候。 這時候我們就可以呼叫 recvfrom函式來將資料拷貝到我們的程式緩衝區中。 IO複用同非阻塞IO本質一樣,不過利用了新的select系統呼叫,由核心來負責本來是請求程序該做的輪詢操作。看似比非阻塞IO還多了一個系統呼叫開銷,不過因為可以支援多路IO,才算提高了效率。多路複用的高階之處在於:它能同時等待多個檔案描述符,而這些檔案描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select()函式就可以返回。

非同步I/O

      當我們執行在非同步 I/O 模式下時,我們如果想進行 I/O 操作,只需要告訴核心我們要進行 I/O 操作,然後核心會馬上返回。具體的 I/O 和資料的拷貝全部由核心來完成,我們的程式可以繼續向下執行。當核心完成所有的 I/O 操作和資料拷貝後,核心將通知我們的程式。 還有一個生動的例子解釋同步、非同步、阻塞和非阻塞 :http://www.cnblogs.com/jogger/archive/2012/09/27/2705386.html