1. 程式人生 > >程序執行緒間的同步機制

程序執行緒間的同步機制

因為是轉載文章, 在此標明出處,如有冒犯請聯絡本人。

因為好的文章,以前只想收藏,但連線有時候會失效,所以現在碰到好的直接轉到自己這裡。

原文出處:http://www.cnblogs.com/memewry/archive/2012/08/22/2651696.html


一、程序/執行緒間同步機制。


臨界區、互斥區、事件、訊號量四種方式
臨界區(Critical Section)、互斥量(Mutex)、訊號量(Semaphore)、事件(Event)的區別
1、臨界區:通過對多執行緒的序列化來訪問公共資源或一段程式碼,速度快,適合控制資料訪問。在任意時刻只允許一個執行緒對共享資源進行訪問,如果有多個執行緒試圖訪問公共資源,那麼在有一個執行緒進入後,其他試圖訪問公共資源的執行緒將被掛起,並一直等到進入臨界區的執行緒離開,臨界區在被釋放後,其他執行緒才可以搶佔。
2、互斥量:採用互斥物件機制。 只有擁有互斥物件的執行緒才有訪問公共資源的許可權,因為互斥物件只有一個,所以能保證公共資源不會同時被多個執行緒訪問。互斥不僅能實現同一應用程式的公共資源安全共享,還能實現不同應用程式的公共資源安全共享 .互斥量比臨界區複雜。因為使用互斥不僅僅能夠在同一應用程式不同執行緒中實現資源的安全共享,而且可以在不同應用程式的執行緒之間實現對資源的安全共享。
3、訊號量:它允許多個執行緒在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大執行緒數目 .訊號量物件對執行緒的同步方式與前面幾種方法不同,訊號允許多個執行緒同時使用共享資源,這與作業系統中的PV操作相同。它指出了同時訪問共享資源的執行緒最大數目。它允許多個執行緒在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大執行緒數目。

PV操作及訊號量的概念都是由荷蘭科學家E.W.Dijkstra提出的。訊號量S是一個整數,S大於等於零時代表可供併發程序使用的資源實體數,但S小於零時則表示正在等待使用共享資源的程序數。
   P操作申請資源:
  (1)S減1;
  (2)若S減1後仍大於等於零,則程序繼續執行;
  (3)若S減1後小於零,則該程序被阻塞後進入與該訊號相對應的佇列中,然後轉入程序排程。
  
  V操作 釋放資源:
  (1)S加1;
  (2)若相加結果大於零,則程序繼續執行;
  (3)若相加結果小於等於零,則從該訊號的等待佇列中喚醒一個等待程序,然後再返回原程序繼續執行或轉入程序排程。
4、事 件: 通過通知操作的方式來保持執行緒的同步,還可以方便實現對多個執行緒的優先順序比較的操作 .

總結:
  1. 互斥量與臨界區的作用非常相似,但互斥量是可以命名的,也就是說它可以跨越程序使用。所以建立互斥量需要的資源更多,所以如果只為了在程序內部是用的話使用臨界區會帶來速度上的優勢並能夠減少資源佔用量。因為互斥量是跨程序的互斥量一旦被建立,就可以通過名字開啟它。
  2. 互斥量(Mutex),訊號燈(Semaphore),事件(Event)都可以被跨越程序使用來進行同步資料操作,而其他的物件與資料同步操作無關,但對於程序和執行緒來講,如果程序和執行緒在執行狀態則為無訊號狀態,在退出後為有訊號狀態。所以可以使用WaitForSingleObject來等待程序和執行緒退出。
  3. 通過互斥量可以指定資源被獨佔的方式使用,但如果有下面一種情況通過互斥量就無法處理,比如現在一位使用者購買了一份三個併發訪問許可的資料庫系統,可以根據使用者購買的訪問許可數量來決定有多少個執行緒/程序能同時進行資料庫操作,這時候如果利用互斥量就沒有辦法完成這個要求,訊號燈物件可以說是一種資源計數器。

二、程序間通訊方式

由於比較容易混淆,我們把程序間通訊方法也列在這裡做比較。
程間通訊就是在不同程序之間傳播或交換資訊,那麼不同程序之間存在著什麼雙方都可以訪問的介質呢?程序的使用者空間是互相獨立的,一般而言是不能互相訪問的,唯一的例外是共享記憶體區。但是,系統空間卻是“公共場所”,所以核心顯然可以提供這樣的條件。除此以外,那就是雙方都可以訪問的外設了。在這個意義上,兩個程序當然也可以通過磁碟上的普通檔案交換資訊,或者通過“登錄檔”或其它資料庫中的某些表項和記錄交換資訊。廣義上這也是程序間通訊的手段,但是一般都不把這算作“程序間通訊”。因為那些通訊手段的效率太低了,而人們對程序間通訊的要求是要有一定的實時性。


 
  程序間通訊主要包括管道, 系統IPC(包括訊息佇列,訊號量,共享儲存), SOCKET.


    管道分為有名管道和無名管道,無名管道只能用於親屬程序之間的通訊,而有名管道則可用於無親屬關係的程序之間。


    訊息佇列用於運行於同一臺機器上的程序間通訊,與管道相似;


  共享記憶體通常由一個程序建立,其餘程序對這塊記憶體區進行讀寫。得到共享記憶體有兩種方式:對映/dev/mem裝置和記憶體映像檔案。前一種方式不給系統帶來額外的開銷,但在現實中並不常用,因為它控制存取的是實際的實體記憶體;
     本質上,訊號量是一個計數器,它用來記錄對某個資源(如共享記憶體)的存取狀況。一般說來,為了獲得共享資源,程序需要執行下列操作:
 
  (1)測試控制該資源的訊號量;
  (2)若此訊號量的值為正,則允許進行使用該資源,程序將進號量減1;
  (3)若此訊號量為0,則該資源目前不可用,程序進入睡眠狀態,直至訊號量值大於0,程序被喚醒,轉入步驟(1);
  (4)當程序不再使用一個訊號量控制的資源時,訊號量值加1,如果此時有程序正在睡眠等待此訊號量,則喚醒此程序。
    套接字通訊並不為Linux所專有,在所有提供了TCP/IP協議棧的作業系統中幾乎都提供了socket,而所有這樣作業系統,對套接字的程式設計方法幾乎是完全一樣的

三、程序/執行緒同步機制與程序間通訊機制比較
很明顯2者有類似,但是差別很大,同步主要是臨界區、互斥、訊號量、事件,程序間通訊是管道、記憶體共享、訊息佇列、訊號量、socket。共通之處是,訊號量和訊息(事件)

其他資料:

程序間通訊(IPC)方法主要有以下幾種:   
    管道/FIFO/共享記憶體/訊息佇列/訊號   

1.管道中還有命名管道和非命名管道(即匿名管道)之分,非命名管道(即匿名管道)只能用於父子程序通訊,命名管道可用於非父子程序,命名管道就是FIFO,管道是先進先出的通訊方式    

2.訊息佇列是用於兩個程序之間的通訊,首先在一個程序中建立一個訊息佇列,然後再往訊息佇列中寫資料,而另一個程序則從那個訊息佇列中取資料。需要注意的是,訊息佇列是用建立檔案的方式建立的,如果一個程序向某個訊息佇列中寫入了資料之後,另一個程序並沒有取出資料,即使向訊息佇列中寫資料的程序已經結束,儲存在訊息佇列中的資料並沒有消失,也就是說下次再從這個訊息佇列讀資料的時候,就是上次的資料!!!!    

3.訊號量,它與WINDOWS下的訊號量是一樣的,所以就不用多說了    

4.共享記憶體,類似於WINDOWS下的DLL中的共享變數,但LINUX下的共享記憶體區不需要像DLL這樣的東西,只要首先建立一個共享記憶體區,其它程序按照一定的步驟就能訪問到這個共享記憶體區中的資料,當然可讀可寫      

以上幾種方式的比較:    

1.管道:速度慢,容量有限,只有父子程序能通訊    

2.FIFO:任何程序間都能通訊,但速度慢    

3.訊息佇列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完資料的問題    

4.訊號量:不能傳遞複雜訊息,只能用來同步    

5.共享記憶體區:能夠很容易控制容量,速度快,但要保持同步,比如一個程序在寫的時候,另一個程序要注意讀寫的問題,相當於執行緒中的執行緒安全,當然,共享記憶體區同樣可以用作執行緒間通訊,不過沒這個必要,執行緒間本來就已經共享了同一程序內的一塊記憶體