“我找到好辦法了!”
沒有想到,說話的人竟然是磁盤!
行程調度器瑟瑟的說:“你有方法?還是算了吧,我怕用你的方法作業系統要亂套了,”
磁盤委屈的道:“不就是剛剛冤枉你了嗎,這么小氣干什么!再說了,這個方法不是我想出來的,是我從檔案里找到的,”
作業系統挑了挑眉毛:“哦?你找到什么檔案了,讓大家也瞅瞅?”
磁盤嗡嗡的轉起來,很快就把檔案取出來了,
“當當當當~ 這可是大師 Dijkstra 的論文,他引入了一個全新的變數型別——信號量(semaphore),然后還為信號量設定了兩種操作,P(proberen,檢測) 和 V(verhogen,增量) ,”
”說清楚點啊,信號量是怎么個用法啊?“行程急切的問道,
“別急,讓我接著看,,,Dijkstra 提出,P操作是檢測信號量是否為正值,如果不是,就阻塞呼叫行程, V操作能喚醒一個阻塞行程,讓他恢復執行 ,具體點的話就是這樣: “
// S 為信號量
P(s):
{
S = S - 1
if (S < 0)
{
呼叫該 P 操作的行程阻塞,并插入相應的阻塞佇列;
}
}
// S 為信號量
V(s):
{
S = S + 1
if (S <= 0)
{
從等待信號量 S 的阻塞佇列里喚醒一個行程;
}
}
記憶體仔細看了代碼,說:”這個實作也要求是原子操作誒,Dijkstra 這個方法很有趣啊,“
行程蒙圈了:“我怎么完全看不懂啊?記憶體你給我講講唄,”
“好,我就用最簡單的一組執行緒舉例子了:
// 執行緒 A,B,C , S = 1
...
P(S) //S = S - 1 若 S < 0 ,阻塞等待
購票操作
V(S) //S = S + 1 若 S <= 0, 表明有執行緒阻塞了,得喚醒其中一個
...
這里的 「購票操作」 就是我們要保護的臨界區,我們要保證一次只能有一個執行緒進入,那我們就把 S 的初始值設為 1 ,當執行緒 A 第一個呼叫 P(S) 后,S 的值就變成了 0 ,A 成功進入臨界區,在 A 出臨界區之前,執行緒 B 如果呼叫 P(S), S 就變成 -1 ,滿足 S < 0 的判斷條件,執行緒 B 就被阻塞了,等 A 呼叫 V(S) 后,S 的值又變成 0 ,滿足 S <= 0,就會把執行緒 B 喚醒,B 就能進入臨界區了,“
行程恍然大悟:“原來是這樣,看起來和二元鎖差不多啊,但是不用忙等待了,”
記憶體神秘一笑:“信號量能做的可不止這些,你想想看,要是我把 S 的初始值設為 2 ,會發生什么?”
“一次能有兩個執行緒訪問臨界區!”行程這次反應快多了:“也就是說 S 的初始值可以控制有多少個執行緒進入臨界區,太厲害了!”
tobe 注:從信號量的值能看出還有多少個行程能進入臨界區,如果為負數,表明有 x 個行程因為呼叫 P(S) 而被阻塞
“沒錯,所以說信號量是一個很靈活的并發機制,而且信號量還有另一個厲害的用處:

你看這兩個行程有什么特別的地方?“
“emmmm,這個嘛,行程 P2 的 V 操作居然放在 P 操作的前面,而且兩個操作的信號量還不是同一個,”
“沒錯,這樣使用信號量,能讓兩個行程做到同步,你看,如果 P1 運行到 P(S1),他是不是會阻塞?”
行程認真一看,說:“沒錯誒,S1 初始值是 0,P1 肯定得停在這一句,讓我再看看,,,如果 P1 想接著運行,就得等 P2 呼叫 V(S1) 把他喚醒,”
“是的,這就是同步——運行快的 P1 必須在這里停下來等 P2 運行到指定位置,兩個行程的執行順序就是這樣:

也就是說 x 最終的值必然是 30,而不可能是 20,在信號量的幫助下,這兩個行程達成了同步,“
行程由衷的感嘆:“信號量實在是太強大了!咱們以后就用信號量來解決互斥的問題吧!”
tobe 注:在 Linux 里提供了信號量和互斥量(也就是二元鎖)這兩種主要機制實作互斥,不過 Linux 的信號量功能要比文章里講得復雜得多,「UNIX 環境高級編程」這本書里寫到「,,,三種特性造成了這種并非必要的復雜性」,對于一般的互斥操作,還是建議使用互斥鎖(當然是阻塞而非忙等待),稍微復雜點的鎖還有「讀寫鎖」,以后有機會再講吧~
覺得我寫的還不錯的話,就點個贊吧!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/67343.html
標籤:C
上一篇:關于鏈表
