多核時代:並行程序設計探討(5)——Windows和Linux對決(進程間同步)
Windows和Linux對決(線程間同步)
1.1 Windows線程同步
1.1.1 關鍵代碼區Critical Section
所謂“關鍵代碼區”,相信大家看名字也能理解個大概了。首先:它很關鍵,第二:它是代碼區。之所以關鍵,當然目的就是每次隻能一個線程能夠進入;既然是代碼區,那就是隻能在一組擁有同樣代碼的線程中用。
那什麼情況下會用到關鍵代碼區呢?當然是要保護多個線程都會用到的東西了,說到這裏,想必你已經猜到了:全局變量和靜態變量。
1.1.2 互斥Mutex
互斥看起來和關鍵代碼區是一樣的,都是每次都是隻允許一個線程使用。但互斥和關鍵代碼區相比,具有如下特點:
對比點 |
關鍵代碼區 |
互斥 |
備注 |
名字 |
無名字 |
有名字 |
NA |
跨進程 |
不能跨進程 |
可以跨進程 |
因為有名字,所以可以跨進程 |
訪問模式 |
沒有超時 |
可以超時 |
NA |
死鎖問題 |
線程掛了其它線程就隻能傻等了 |
線程掛了,操作係統會通知其它線程 |
NA |
運行環境 |
用戶區 |
內核區 |
所以關鍵代碼區性能要高一些。 |
1.1.3 信號量Semaphore
信號量本質上就是一個計數器,當計數器大於0時就意味著被保護的對象可用。每次申請計數器就減1,釋放就加1.
信號量和互斥體相比,一個最明顯的差別就在於互斥體每次隻能有一個線程進行訪問,而信號量可以有多個線程進行訪問。
看到這裏,大家可能都像我開始一樣存在這樣的問題:如果將信號量最大值設置為1,那麼不就是相當於互斥量了嗎?
看起來是一樣的,而且在有些係統上也確實是這樣的,據說是互斥體底層就是信號量來實現的,或者幹脆就沒有互斥體(例如傳統UNIX),但在有的係統上還是有差別的,差別在於:申請和釋放是否要同一個線程完成,Windows就是這種形式。互斥體要求同一線程來申請和釋放,而信號量就可以由不同的線程申請和釋放(但是我很難想象這樣做有什麼好處,難倒要給一個線程集中獲取信號量,再來通知另外的線程工作?)。
1.1.4 事件Event
事件本質上是一個係統信號,即:發生了某件事情後,發一個信號給其它關心這件事情的線程。
從事件的本質上來看,事件不是為了資源保護的,而是為了線程間通知用的。舉個簡單的例子:Socket接收完一個消息後,將其放入隊列,然後需要通知消息處理線程進行處理。
大家想想,如果沒有事件通知會怎麼樣呢?那接收線程隻能設一個定時器或者循環,定時甚至循環去查詢隊列中是否有消息,這種定時和循環處理是對係統性能的極大浪費,所以,有了事件後,就不用這麼浪費了。
1.2 Linux線程同步
介紹完Windows,Linux介紹就很方便了,就像上一篇博文提到的一樣,Windows和Linux其實很多地方相似,線程同步也不例外。
1.2.1 關鍵代碼區???
不好意思,Linux沒有這個東東。
1.2.2 互斥Mutex
Linux和Windows是一樣的,這裏就不詳細介紹了,需要注意的是傳統UNIX並沒有互斥這個東東,傳統UNIX的互斥是通過二元信號量(即最大值為1)來實現的。
1.2.3 信號量Semaphore
需要注意的是Linux中信號量有兩種:一種是內核POSIX標準的信號量,一種是用戶態的傳統UNIX IPC信號量。兩者的差別如下:
對比點 |
POSIX Semaphore |
IPC Semaphore |
備注 |
控製者 |
內核 |
用戶 |
IPC Semaphore可以通過semctrl函數修改對外表現。 |
權限控製 |
不允許修改 |
用戶可修改 |
NA |
性能 |
優於IPC |
劣於POSIX |
NA |
範圍 |
進程級 |
係統級 |
如果進程退出時忘記關閉,POSIX會自動釋放。 |
POSIX信號量和Windows的信號量是一樣的。
1.2.4 條件變量Conditions
看到這個名字有點莫名其妙,條件變量和線程同步有什麼關係呢?
但其實是Linux(或者是POSIX)的名字取得不好才導致我們很難理解,本質上條件變量就是Windows的事件,作用也是一樣的。唉,如果Linux或者POSIX不想和Windows同名,改成叫“通知”也能讓我們這些小蝦多省點腦力啊:)
1.2.5 信號Signal
類似於“共享內存”也是一種進程間通信的方式一樣,我把信號也列進來作為線程同步的一種,因為本質上信號不是為了線程間同步而設計的,但我們可以利用其作為線程同步來使用。
如何使用信號呢?既然信號本身就是一種通知(還記得上麵我建議將“條件變量”建議改名為什麼嗎?),那我們就按照通知來使用了,例如:A做完了某事,發一個信號給B,B收到後開始啟動做另外一件事。
請注意:和“條件變量”不同的是,條件變量支持廣播機製,而信號隻能是點對點,因此實際使用中應該還是“條件變量”方便一些。當然如果是傳統UNIX,那就隻能利用信號來進行通知了。
===============================================================================
注:看我的博客的朋友可能會發現一個現象,我幾乎從來不介紹詳細的函數或者API,而基本上都在“歸納、總結、對比”。這是我個人的一個風格或者理解吧,我認為函數或者API用的時候查一下就可以了,而在分析和設計的時候,關鍵是要知道有哪些東西可以給我們用,而且要知道我們具體究竟應該用哪個,因此在平時就必須多歸納、總結、對比,而不是背住各種函數和API。
==========================未完待續===============================
最後更新:2017-04-02 03:42:38