2008-08-29 69 views
253

信號量是一種常用於解決多線程問題的編程概念。我向社區提出的問題:什麼是信號量?

什麼是信號燈,以及如何使用它?

+9

一個布爾標誌,其值取決於整數計數器是否達到其指定的上限。混淆到最大! – Sam 2011-09-19 01:02:00

回答

0

信號量是一種鎖定資源的方式,以確保在執行一段代碼時,只有這段代碼才能訪問該資源。這使得兩個線程不會同時訪問資源,這可能會導致問題。

+11

聽起來像一個互斥體而不是信號量 – Sam 2011-09-19 00:57:52

16

@Craig:

信號量是鎖定一個 資源,使得可以保證 在執行一段代碼, 僅這段代碼訪問 該資源的方式。這使得兩個線程 不會同時訪問可能導致問題的資源 。

這不僅限於一個線程。信號量可以配置爲允許固定數量的線程訪問資源。

+2

這是一個評論,而不是一個答案。 – kaspersky 2013-05-19 17:15:40

+7

是的,但我認爲在評論被添加到堆棧溢出之前我寫了這個。或者我沒有,不記得。這次我在評論中回答。 :-) – 2013-09-15 21:36:40

+0

當你意識到答案的尷尬時刻在2008年發佈。懷舊... – 2017-10-21 02:05:41

136

邁克爾巴爾的文章Mutexes and Semaphores Demystified是一個很好的簡短介紹什麼使互斥和信號量不同,什麼時候它們應該和不應該使用。我在這裏摘錄了幾個關鍵段落。

關鍵是應該使用互斥鎖來保護共享資源,而信號量應該用於信令。您通常不應該使用信號量來保護共享資源,也不會使用信號互斥。例如,在使用信號量來保護共享資源方面,保鏢類比存在一些問題 - 您可以以這種方式使用它們,但這可能很難診斷錯誤。

儘管互斥鎖和信號量在實現上有一些相似之處,但它們的使用方式應始終不同。

最常見的(但仍然不正確)回答頂部提出的問題是互斥體和信號量非常相似,唯一的顯着區別是信號量可以計數高於1。幾乎所有的工程師似乎都正確地理解互斥鎖是一種二進制標誌,通過確保代碼的關鍵部分之間的相互排斥來保護共享資源。但是當被要求擴展如何使用「計數信號量」時,大多數工程師只會根據他們的信心程度不同而表達一些教科書觀點的味道,這些觀點用於保護若干等效資源。

...

此時一個有趣的比喻是用浴室鍵的主意,因爲保護共享資源做 - 浴室。如果一家商店有一間浴室,那麼一個密鑰就足以保護該資源並防止多人同時使用它。

如果有多個浴室,可能會試圖鎖定他們並製作多個鍵 - 這與信號量被誤用相似。一旦你有了鑰匙,你實際上並不知道哪個浴室可用,如果沿着這條路走下去,你可能最終會使用互斥體來提供這些信息,並確保你不會佔用已經佔用的浴室。

信號量是保護幾個本質上相同的資源的錯誤工具,但這是有多少人想到它並使用它。保鏢類比明顯不同 - 不存在幾種相同類型的資源,而是有一種資源可以接受多個併發用戶。我認爲在這種情況下可以使用信號量,但很少有類比實際存在的現實世界情況 - 更常見的是有幾種相同的類型,但仍然是單獨的資源,如浴室,這些資源無法使用這條路。

...

正確使用信號燈的是信號從一個任務到另一個。每個使用它所保護的共享資源的任務都會按照該順序執行和釋放互斥鎖。相比之下,使用信號量的任務不是信號就是等待 - 不是兩者都有。例如,任務1可以包含用於在按下「電源」按鈕時發佈(即,信號或增加)特定信號量的代碼,並且喚醒顯示器的任務2依賴於相同的信號量。在這種情況下,一個任務是事件信號的生產者;另一個消費者。

...

這裏很重要的一點是提出,互斥干擾實時操作系統在一個糟糕的方式,導致其中一個不太重要的任務可能因爲之前的一個更重要的任務執行優先級反轉資源共享。簡而言之,當較低優先級的任務使用互斥鎖來獲取資源A時,會發生這種情況,然後嘗試抓取B,但由於B不可用而暫停。當它在等待時,一個更高優先級的任務就會出現,並且需要A,但它已經被綁定了,並且一個甚至沒有運行的進程,因爲它正在等待B.有很多方法可以解決這個問題,但它通常是固定的通過改變互斥和任務管理器。在這些情況下,互斥體比二進制信號量複雜得多,在這種情況下使用信號量會導致優先級反轉,因爲任務管理器不知道優先級反轉,並且無法採取措施糾正它。

...

互斥體和信號量之間的廣泛的現代混亂的原因是歷史的,因爲它的日期,一路回到1974年發明的信號量(大寫的「S」的,在這篇文章中)由Djikstra。在此之前,計算機科學家已知的中斷安全任務同步和信號傳輸機制都不能有效擴展以供兩項以上任務使用。 Dijkstra的革命性,安全和可擴展的Semaphore應用於臨界區保護和信號傳輸。因此混亂開始了。然而,隨着基於優先級的搶佔式RTOS(例如VRTX,大約1980年)的出現,建立RMA的學術論文的發表以及由優先級反轉引起的問題,後來它對於操作系統開發者而言變得顯而易見,並且一份關於1990年優先權繼承協議的文件,3顯而易見,互斥量必須不僅僅是具有二進制計數器的信號量。

互斥:資源共享

信號燈:信號

不要使用一個對於其他沒有仔細考慮的副作用。

+6

看看這個斯坦福併發PDF文件。看第8頁。上面的解釋將會更有意義。然後 https://see.stanford.edu/materials/icsppcs107/23-Concurrency-Examples.pdf – 2016-02-23 01:13:46

58

互斥:獨家成員訪問資源

信號燈:正成員對資源的訪問

也就是說,一個互斥體可用於syncronize訪問計數器,文件,數據庫等

一個sempahore可以做同樣的事情,但支持固定數量的同時呼叫者。例如,我可以將數據庫調用包裝在一個信號量(3)中,以便多線程應用程序最多可以同時連接3個數據庫。所有嘗試都會阻止,直到三個插槽中的一個打開。他們讓事情像做天真的節流真的很容易。

4

信號量是一個包含自然數(即大於或等於零的整數)的對象,其上定義了兩個修改操作。一個操作,V,自然增加1。另一個操作P將自然數減少1.兩個活動都是原子的(即,不能與VP同時執行其他操作)。

由於自然數0不能降低,在含有0會阻塞調用過程(/線程)的執行,直到某一時刻在其數目是不再0和P可以成功旗語主叫P(和原子)執行。

正如其他答案中所述,信號量可用於將對特定資源的訪問限制爲最大(但可變)數量的進程。

313

將信號量想象成夜總會的保鏢。俱樂部一次允許有一定數量的專用人員。如果俱樂部已滿,則不允許任何人進場,但一旦有人離開另一人可能進場。

這只是一種限制特定資源消費者數量的方法。例如,限制應用程序中同時調用數據庫的次數。

這是在C#中一個非常教學例子:-)

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Threading; 

namespace TheNightclub 
{ 
    public class Program 
    { 
     public static Semaphore Bouncer { get; set; } 

     public static void Main(string[] args) 
     { 
      // Create the semaphore with 3 slots, where 3 are available. 
      Bouncer = new Semaphore(3, 3); 

      // Open the nightclub. 
      OpenNightclub(); 
     } 

     public static void OpenNightclub() 
     { 
      for (int i = 1; i <= 50; i++) 
      { 
       // Let each guest enter on an own thread. 
       Thread thread = new Thread(new ParameterizedThreadStart(Guest)); 
       thread.Start(i); 
      } 
     } 

     public static void Guest(object args) 
     { 
      // Wait to enter the nightclub (a semaphore to be released). 
      Console.WriteLine("Guest {0} is waiting to entering nightclub.", args); 
      Bouncer.WaitOne();   

      // Do some dancing. 
      Console.WriteLine("Guest {0} is doing some dancing.", args); 
      Thread.Sleep(500); 

      // Let one guest out (release one semaphore). 
      Console.WriteLine("Guest {0} is leaving the nightclub.", args); 
      Bouncer.Release(1); 
     } 
    } 
} 
11

信號量也可以用作...信號燈。 例如,如果您有多個進程將數據排入隊列,並且只有一個任務使用隊列中的數據。如果您不希望消費任務持續輪詢隊列中的可用數據,則可以使用信號量。

這裏信號量不是用作排斥機制,而是用作信號機制。 消費任務正在等待信號量 生產任務正在信號量上發佈。

這樣的耗時的任務運行時,只有當沒有要出隊

1

硬件或軟件的標誌數據。在多任務處理系統中,信號量是可變的,具有指示公共資源狀態的值。需要資源的進程檢查信號量以確定資源狀態,然後決定如何繼續。

0

這是一個老問題,但信號量的最有趣的用途之一是讀/寫鎖,它沒有被明確提及。

這些讀寫鎖的工作原理很簡單:爲讀者使用一個許可證,併爲作者使用所有許可證。 事實上,一個r/w鎖的簡單實現,但需要讀取(實際上兩次)的元數據修改,可能會成爲瓶頸,仍然明顯好於互斥鎖或鎖。

另一個缺點是作者可以很容易地啓動,除非信號量是公平的,或者寫入在多個請求中獲得許可,在這種情況下,他們之間需要明確的互斥。

而且read

1

所以,想象每個人都試圖去洗手間並且只需按鍵衛生間一定數量。現在如果沒有足夠的按鍵,那個人需要等待。所以把信號量看作代表那些可供浴室使用的密鑰集合(系統資源),以便不同的進程(衛浴業者)可以請求訪問。

現在設想兩個進程試圖同時去衛生間。這不是一個好的情況,信號燈用於防止這種情況。不幸的是,信號量是一種自願的機制和過程(我們的浴室觀衆)可以忽略它(即使有鑰匙,有人仍然可以打開門)。

計數信號量的二進制/互斥量爲&也有差異。

查看http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.html的講義。

10

構建併發程序有兩個基本概念 - 同步和互斥。我們將看到這兩種類型的鎖(信號量通常是一種鎖定機制)如何幫助我們實現同步和互斥。

信號量是一種編程結構,通過實現同步和互斥,幫助我們實現併發。信號量有兩種類型,二進制和計數。

信號量有兩部分:計數器和等待訪問特定資源的任務列表。信號量執行兩個操作:wait(P)[這就像獲取一個鎖],並釋放(V)[類似於釋放一個鎖] - 這是信號量中唯一可以執行的兩個操作。在二進制信號量中,計數器在邏輯上介於0和1之間。你可以把它看作與具有兩個值的鎖相似:open/closed。計數信號量有多個計數值。

重要的是要理解的是,信號量計數器跟蹤不必阻止任務的數量,即他們可以取得進展。只有當計數器爲零時,任務纔會阻塞,並將自己添加到信號量列表中。因此,如果任務無法進行,則會將任務添加到P()例程的列表中,並使用V()例程「釋放」任務。

現在,瞭解如何使用二進制信號來解決同步和互斥 - 它們本質上是鎖定是非常明顯的。

ex。同步:

thread A{ 
semaphore &s; //locks/semaphores are passed by reference! think about why this is so. 
A(semaphore &s): s(s){} //constructor 
foo(){ 
... 
s.P(); 
;// some block of code B2 
... 
} 

//thread B{ 
semaphore &s; 
B(semaphore &s): s(s){} //constructor 
foo(){ 
... 
... 
// some block of code B1 
s.V(); 
.. 
} 

main(){ 
semaphore s(0); // we start the semaphore at 0 (closed) 
A a(s); 
B b(s); 
} 

在上面的例子中,B2只能在B1完成執行後才能執行。假設線程A先執行 - 進入sem.P()並等待,因爲計數器爲0(關閉)。線程B出現,完成B1,然後釋放線程A - 然後完成B2。所以我們實現同步。

現在讓我們來看看互斥與二進制信號:

thread mutual_ex{ 
semaphore &s; 
mutual_ex(semaphore &s): s(s){} //constructor 
foo(){ 
... 
s.P(); 
//critical section 
s.V(); 
... 
... 
s.P(); 
//critical section 
s.V(); 
... 

} 

main(){ 
semaphore s(1); 
mutual_ex m1(s); 
mutual_ex m2(s); 
} 

互斥很簡單以及 - M1,並在同一時間M2不能進入臨界區。所以每個線程都使用相同的信號量爲其兩個關鍵部分提供相互排斥。現在,是否有可能具有更高的併發性?取決於關鍵部分。 (想想如何使用信號量來實現互斥......提示提示:我是否只需要使用一個信號量?)

計數信號量:具有多個值的信號量。讓我們看看這是什麼意思 - 一個具有多個值的鎖?所以打開,關閉,...嗯。用於互斥或同步的多階段鎖定有什麼用處?

讓我們以兩個簡單:用一個計數信號

同步:比方說,你有3個任務 - #1和2在要3.你會如何設計你的同步後執行?

thread t1{ 
... 
s.P(); 
//block of code B1 

thread t2{ 
... 
s.P(); 
//block of code B2 

thread t3{ 
... 
//block of code B3 
s.V(); 
s.V(); 
} 

因此,如果您的信號量開始關閉,您可以確保t1和t2塊被添加到信號量列表中。然後,所有重要的t3,完成其業務,並釋放t1和t2。他們釋放了什麼命令?取決於信號量列表的實施。可能是FIFO,可能基於某些特定的優先級等。 (注意:考慮如何安排你的P和V;如果你想以某種特定的順序執行t1和t2,以及如果你不知道信號量的實現)

(Find out :如果V的數量大於P的數量,會發生什麼?)

相互排斥使用計數信號量:我想讓您爲此構建自己的僞代碼(讓您更好地理解事物!) - 但是基本概念是這樣的:counter = N的計數信號允許N個任務自由地進入臨界區。這意味着你有N個任務(或線程,如果你喜歡的話)進入關鍵部分,但是第N + 1個任務被阻塞(進入我們最喜歡的阻塞任務列表),只有當某個V的信號量至少一次。所以信號量計數器,而不是在0和1之間擺動,現在在0和N之間,允許N個任務自由進入和退出,阻止任何人!

現在天哪,你爲什麼需要這樣一個愚蠢的東西?互相排斥的關鍵不是讓一個人獲得資源嗎? (提示提示......你並不總是隻有一個驅動器在你的電腦,你......?)

要想想:是互斥的信號量僅具有計數實現?如果你有10個資源實例,並且有10個線程進入(通過計數信號量)並嘗試使用第一個實例會怎麼樣?

2

考慮一下,出租車。出租車最多可容納3名(後方)+2(前方)人員,包括司機。所以,信號量一次只允許一個車內有5個人。 一個互斥體只允許一個人坐在車內的座位上。 所以,互斥體是允許資源的獨佔訪問。 信號量允許一次訪問n個資源。

0

這裏是關於旗語一些更有趣的知識:

信號量是一個編程架構E. W. Dijkstra算法在60年代末設計的。迪克斯特拉的模式是鐵路運營:考慮一段鐵路,其中有一條單一軌道,一次只允許有一列火車。

保護這條賽道是一個信號量。在進入單軌之前,列車必須等待,直到信號燈處於允許旅行的狀態。當列車進入軌道時,信號燈改變狀態以防止其他列車進入軌道。一列正在離開這段軌道的火車必須再次改變信號燈的狀態,以允許另一列火車進入。

在計算機版本中,信號量似乎是一個簡單的整數。一個線程等待進程的許可,然後通過在信號量上執行P操作發出信號。

該操作的語義是這樣的,線程必須等到信號量的值爲正,然後通過減去信號量的值來改變信號量的值。完成後,線程執行V操作,通過向信號量的值加1來改變信號量的值。這些操作以原子方式進行至關重要 - 它們不能被細分爲在信號量之間可發生其他動作的片斷。在P操作中,信號量的值在遞減之前必須爲正值(導致一個值保證爲非負值,一個值比遞減前的值小)。

在P和V操作中,算術運算都必須無干擾地進行。如果兩個V操作在同一個信號量上同時執行,則淨效應應該是信號量的新值比它大兩倍。

由於Dijkstra是荷蘭人,因此P和V的助記符意義對世界大多數人來說並不清楚。然而,爲了獲得真正的獎學金:P代表prolagen,一個來自proberen te verlagen的僞造詞,意思是試圖減少。 V代表verhogen,意思是增加。這在Dijkstra的技術筆記之一EWD 74中有討論。

sem_wait(3RT)和sem_post(3RT)對應於Dijkstra的P和V操作。 sem_trywait(3RT)是P操作的條件形式:如果調用線程無法等待信號量減少,則調用立即返回非零值。

有兩種基本的信號量:二進制信號,從未採取零或一個其他值,和計數信號量,可以採取任意非數值。二進制信號量在邏輯上就像一個互斥量。

但是,雖然它沒有強制執行,但互斥鎖只能由持有鎖的線程解鎖。沒有「持有信號量的線程」的概念,所以任何線程都可以執行V(或sem_post(3RT))操作。

計數信號量和條件變量一樣強大(與互斥量一起使用)。在許多情況下,使用計數信號量而不是條件變量來實現代碼可能會更簡單(如下幾個示例所示)。

但是,當一個互斥量與條件變量一起使用時,會隱含一個包圍 - 它清楚程序的哪一部分正在受到保護。對於信號量來說,情況並非一定如此,這可能被稱爲併發編程 - 它非常強大,但卻非常容易以非結構化,不確定的方式使用。

2

在編程中,特別是在Unix系統中,信號量是一種技術,用於協調或同步多個進程競爭相同操作系統資源的活動。

信號量是操作系統存儲器中指定位置的值,每個進程都可以檢查並更改。根據找到的值,流程可以使用資源或意識到它已被使用,並在再次嘗試之前等待一段時間。

信號可以是二進制(0或1)或可以有附加值。通常,使用信號量的進程會檢查該值,然後如果使用該資源,則更改該值以反映此值,以便隨後的信號量用戶知道要等待。

信號量通常用於兩個目的: 1)共享公用內存空間 2)共享文件訪問。

信號量是進程間通信(IPC)技術之一。

C編程語言提供了一組用於管理信號量的接口或「函數」。

相關問題