1. 程式人生 > >理解Semaphore及其用法詳解

理解Semaphore及其用法詳解

Mutex是一把鑰匙,一個人拿了就可進入一個房間,出來的時候把鑰匙交給佇列的第一個。一般的用法是用於序列化對critical section程式碼的訪問,保證這段程式碼不會被並行的執行。



Semaphore是一件可以容納N人的房間,如果人不滿就可以進去,如果人滿了,就要等待有人出來。對於N=1的情況,稱為binary semaphore。一般的用法是,用於限制對於某一資源的同時訪問。



Binary semaphoreMutex的差異:

在 有的系統中Binary semaphore與Mutex是沒有差異的。在有的系統上,主要的差異是mutex一定要由獲得鎖的程序來釋放。而semaphore可以由其它程序釋 放(這時的semaphore實際就是個原子的變數,大家可以加或減),因此semaphore可以用於程序間同步。Semaphore的同步功能是所有 系統都支援的,而Mutex能否由其他程序釋放則未定,因此建議mutex只用於保護critical section。而semaphore則用於保護某變數,或者同步。



關於semaphore和mutex的區別,網上有著名的廁所理論(http://koti.mbnet.fi/niclasw/MutexSemaphore.html):

Mutex:Is a key to a toilet. One person can have the key - occupy the toilet - at the time. When finished, the person gives (frees) the key to the next person in the queue.Officially: “Mutexes are typically used to serialise access to a section of re-entrant code that cannot be executed concurrently by more than one thread. A mutex object only allows one thread into a controlled section, forcing other threads which attempt to gain access to that section to wait until the first thread has exited from that section.”
Ref: Symbian Developer Library(A mutex is really a semaphore with value 1.)

Semaphore:

Is the number of free identical toilet keys. Example, say we have four toilets with identical locks and keys. The semaphore count - the count of keys - is set to 4 at beginning (all four toilets are free), then the count value is decremented as people are coming in. If all toilets are full, ie. there are no free keys left, the semaphore count is 0. Now, when eq. one person leaves the toilet, semaphore is increased to 1 (one free key), and given to the next person in the queue.

Officially: “A semaphore restricts the number of simultaneous users of a shared resource up to a maximum number. Threads can request access to the resource (decrementing the semaphore), and can signal that they have finished using the resource (incrementing the semaphore).”
Ref: Symbian Developer Library

所以,mutex就是一個binary semaphore (值就是0或者1)。但是他們的區別又在哪裡呢?主要有兩個方面:

    * 初始狀態不一樣:mutex的初始值是1(表示鎖available),而semaphore的初始值是0(表示unsignaled的狀態)。隨後的操 作基本一樣。mutex_lock和sem_post都把值從0變成1,mutex_unlock和sem_wait都把值從1變成0(如果值是零就等 待)。初始值決定了:雖然mutex_lock和sem_wait都是執行V操作,但是sem_wait將立刻將當前執行緒block住,直到有其他執行緒 post;mutex_lock在初始狀態下是可以進入的。
    * 用法不一樣(對稱 vs. 非對稱):這裡說的是“用法”。Semaphore實現了signal,但是mutex也有signal(當一個執行緒lock後另外一個執行緒 unlock,lock住的執行緒將收到這個signal繼續執行)。在mutex的使用中,模型是對稱的。unlock的執行緒也要先lock。而 semaphore則是非對稱的模型,對於一個semaphore,只有一方post,另外一方只wait。就拿上面的廁所理論來說,mutex是一個鑰 匙不斷重複的使用,傳遞在各個執行緒之間,而semaphore擇是一方不斷的製造鑰匙,而供另外一方使用(另外一方不用歸還)。

前面的實驗證明,mutex確實能夠做到post和wait的功能,只是大家不用而已,因為它是“mutex”不是semaphore。


下面給出一個例子:

要 讓一個thread在背景不斷的執行,最簡單的方式就是在該thread執行無窮迴圈,如while(1) {},這種寫法雖可行,卻會讓CPU飆高到100%,因為CPU一直死死的等,其實比較好的方法是,背景平時在Sleep狀態,當前景呼叫背景時,背景馬 上被喚醒,執行該做的事,做完馬上Sleep,等待前景呼叫。當背景sem_wait()時,就是馬上處於Sleep狀態,當前景sem_post() 時,會馬上換起背景執行,如此就可避免CPU 100%的情形了。


/*

*//*
    (C) OOMusou 2006 http://oomusou.cnblogs.com

     Filename : pthread_create_semaphore.cpp
     Compiler : gcc 4.10 on Fedora 5 / gcc 3.4 on Cygwin 1.5.21
     Description : Demo how to create thread with semaphore in Linux.
     Release : 12/03/2006
     Compile : g++ -lpthread pthread_create_semaphore.cpp
    *
/
#include <stdio.h> // printf(),
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <pthread.h> // pthread_create(), pthread_join()
#include <semaphore.h> // sem_init()

sem_t binSem;

void* helloWorld(void* arg);

int main() {
     // Result for System call
    int res = 0;

     // Initialize semaphore
     res =
sem_init(&binSem, 0, 0);
    if (res) {
         printf("Semaphore initialization failed!!/n");
         exit(EXIT_FAILURE);
     }

     // Create thread
     pthread_t thdHelloWorld;
     res = pthread_create(&thdHelloWorld, NULL, helloWorld, NULL);
    if (res) {
         printf("Thread creation failed!!/n");
         exit(EXIT_FAILURE);
     }

    while(1) {
         // Post semaphore
         sem_post(&binSem);
         printf("In main, sleep several seconds./n");
        sleep(1);
     }

     // Wait for thread synchronization
     void *threadResult;
     res = pthread_join(thdHelloWorld, &threadResult);
    if (res) {
         printf("Thread join failed!!/n");
         exit(EXIT_FAILURE);
     }

     exit(EXIT_SUCCESS);
}

void* helloWorld(void* arg) {
    while(1) {
         // Wait semaphore
         sem_wait(&binSem);
         printf("Hello World/n");
     }
}


編譯執行:


[[email protected] semaphore]# gcc semaphore.c -lpthread
[[email protected] semaphore]# ./a.out
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World

semaphore

  訊號量(Semaphore),有時被稱為訊號燈,是在多執行緒環境下使用的一種設施, 它負責協調各個執行緒, 以保證它們能夠正確、合理的使用公共資源。
  什麼是訊號量(Semaphore0
  Semaphore分為單值和多值兩種,前者只能被一個執行緒獲得,後者可以被若干個執行緒獲得。
  以一個停車場是運作為例。為了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這是如果同時來了五輛車,看門人允許其中三輛不受阻礙的進入,然後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,開啟車攔,放入一輛,如果又離開兩輛,則又可以放入兩輛,如此往復。
  在這個停車場系統中,車位是公共資源,每輛車好比一個執行緒,看門人起的就是訊號量的作用。
  更進一步,訊號量的特性如下:訊號量是一個非負整數(車位數),所有通過它的執行緒(車輛)都會將該整數減一(通過它當然是為了使用資源),當該整數值為零時,所有試圖通過它的執行緒都將處於等待狀態。在訊號量上我們定義兩種操作: Wait(等待) 和 Release(釋放)。 當一個執行緒呼叫Wait等待)操作時,它要麼通過然後將訊號量減一,要麼一自等下去,直到訊號量大於一或超時。Release(釋放)實際上是在訊號量上執行加操作,對應於車輛離開停車場,該操作之所以叫做“釋放”是應為加操作實際上是釋放了由訊號量守護的資源。
  實現
  大家都知道,.Net Framework類庫中提供的執行緒同步設施包括:
  Monitor, AutoResetEvent, ManualResetEvent,Mutex,ReadWriteLock和 InterLock。 其中 AutoResetEvent, ManualResetEvent,Mutex派生自WaitHandler,它們實際上是封裝了作業系統提供的核心物件。而其它的應當是在.Net虛擬機器中土生土長的。顯然來自作業系統核心物件的設施使用起來效率要差一些。不過效率並不是我們這裡要考慮的問題,我們將使用兩個 Monitor 和 一個ManualResetEvent 物件來模擬一個訊號量。
  程式碼如下:
  public class Semaphore
  {
      private ManualResetEvent waitEvent = new ManualResetEvent(false);
      private object syncObjWait = new object();
      private int maxCount = 1; file://最大資源數
      private int currentCount = 0; file://當前資源數
      public Semaphore()
  {
  }
     public Semaphore( int maxCount )
  {
     this.maxCount = maxCount;
  }
  public bool Wait()
  {
     lock( syncObjWait ) file://只能一個執行緒進入下面程式碼
     {
         bool waitResult = this.waitEvent.WaitOne(); file://在此等待資源數大於零
         if( waitResult )
         {
           lock( this )
             {
               if( currentCount > 0 )
                 {
                    currentCount--;
                    if( currentCount == 0 )
                     {
                         this.waitEvent.Reset();
                     }
                 }
           else
            {
                 System.Diagnostics.Debug.Assert( false, "Semaphore is not allow current count < 0" );
            }
        }
    }
     return waitResult;
   }
  }
  /**//// <summary>
  /// 允許超時返回的 Wait 操作
  /// </summary>
  /// <param name="millisecondsTimeout"></param>
  /// <returns></returns>
  public bool Wait( int millisecondsTimeout )
  {
       lock( syncObjWait ) // Monitor 確保該範圍類程式碼在臨界區內
          {
             bool waitResult = this.waitEvent.WaitOne(millisecondsTimeout,false);
            if( waitResult )
            {
               lock( this )
               {
                  if( currentCount > 0 )
                  {
                      currentCount--;
                      if( currentCount == 0 )
                       {
                          this.waitEvent.Reset();
                        }
                    }
                 else
                 {
                       System.Diagnostics.Debug.Assert( false, "Semaphore is not allow current count < 0" );
                  }
               }
            }
        return waitResult;
       }
  }
  public bool Release()
  {
          lock( this ) // Monitor 確保該範圍類程式碼在臨界區內
         {
             currentCount++;
             if( currentCount > this.maxCount )
            {
               currentCount = this.maxCount;
               return false;
            }
             this.waitEvent.Set(); file://允許呼叫Wait的執行緒進入
          }
        return true;
       }
  }

相關推薦

[linux下]理解Semaphore及其用法

Mutex是一把鑰匙,一個人拿了就可進入一個房間,出來的時候把鑰匙交給佇列的第一個。一般的用法是用於序列化對critical section程式碼的訪問,保證這段程式碼不會被並行的執行。Semaphore是一件可以容納N人的房間,如果人不滿就可以進去,如果人滿了,就要等待有人出來。對於N=1的情況,稱為bi

理解Semaphore及其用法

Mutex是一把鑰匙,一個人拿了就可進入一個房間,出來的時候把鑰匙交給佇列的第一個。一般的用法是用於序列化對critical section程式碼的訪問,保證這段程式碼不會被並行的執行。Semaphore是一件可以容納N人的房間,如果人不滿就可以進去,如果人滿了,就要等待有人出來。對於N=1的情況,稱為bin

fgets函式及其用法

雖然用 gets() 時有空格也可以直接輸入,但是 gets() 有一個非常大的缺陷,即它不檢查預留儲存區是否能夠容納實際輸入的資料,換句話說,如果輸入的字元數目大於陣列的長度,gets 無法檢測到這個問題,就會發生記憶體越界,所以程式設計時建議使用 fgets()。 fgets() 的原型為: 1

iOS中nil、Nil、NULL和NSNull的理解用法

nil 這個也是條件編譯的,那麼對於我們Objective-C開發來說,nil就代表((void *)0)。 我們使用nil表示Objective-C物件為空,如NSString *str = nil。 我們給物件賦值時一般會使用object = nil,表示我想把這個物件釋放掉;

JavaScript中return的用法

style 返回 www log tle blog 意思 charset fun 1、定義:return 從字面上的看就是返回,官方定義return語句將終止當前函數並返回當前函數的值,可以看下下面的示例代碼: <!DOCTYPE html><html l

SVN trunk(主線) branch(分支) tag(標記) 用法和詳細操作步驟

trac load mar span 必須 最可 objc copy 右鍵 原文地址:http://blog.csdn.net/vbirdbest/article/details/51122637 使用場景: 假如你的項目(這裏指的是手機客戶端項目)的某個版本(例如1.0

js 定時器用法——setTimeout()、setInterval()、clearTimeout()、clearInterval()

ntb 幫助 .get tint num 用法 -c 函數 tel 在js應用中,定時器的作用就是可以設定當到達一個時間來執行一個函數,或者每隔幾秒重復執行某段函數。這裏面涉及到了三個函數方法:setInterval()、setTimeout()、clearI

selenium用法

key url enc element api code 需要 int question selenium用法詳解 selenium主要是用來做自動化測試,支持多種瀏覽器,爬蟲中主要用來解決JavaScript渲染問題。 模擬瀏覽器進行網頁加載,當requests,url

C# ListView用法

ont 結束 server 發生 匹配 鼠標 之前 小圖標 order 一、ListView類 1、常用的基本屬性: (1)FullRowSelect:設置是否行選擇模式。(默認為false) 提示:只有在Details視圖該屬性才有意義

linux cp命令參數及用法---linux 復制文件命令cp

linux file linux cp命令參數及用法詳解---linux 復制文件命令cp [root@Linux ~]# cp [-adfilprsu] 來源檔(source) 目的檔(destination)[root@linux

Python數據類型方法簡介一————字符串的用法

python 字符串連接 字符串用法 符串是Python中的重要的數據類型之一,並且字符串是不可修改的。 字符串就是引號(單、雙和三引號)之間的字符集合。(字符串必須在引號之內,引號必須成對)註:單、雙和三引號在使用上並無太大的區別; 引號之間可以采取交叉使用的方式避免過多轉義;

C# ListView用法(轉)

分組 創建 cti 排列 checkbox 定義 com 程序 erl 一、ListView類 1、常用的基本屬性: (1)FullRowSelect:設置是否行選擇模式。(默認為false) 提示:只有在Details視圖該屬性才有

java中的instanceof用法

定義 xtend print 繼承 interface 參數 保留 如果 ack   instanceof是Java的一個二元操作符(運算符),也是Java的保留關鍵字。它的作用是判斷其左邊對象是否為其右邊類的實例,返回的是boolean類型的數據。用它來判斷某個對象是否是

@RequestMapping 用法

同時 get() turn example track find 說明 tex -h 簡介: @RequestMapping RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。

Css中路徑data:image/png;base64的用法 (轉載)

javascrip base64編碼 asc cda 文件的 color 情況 ont 背景圖片 大家可能註意到了,網頁上有些圖片的src或css背景圖片的url後面跟了一大串字符,比如: background-image:url(data:image/png;bas

global用法

global 在函數內傳遞參數1、global一般用在函數內,將外部變量參數傳遞至函數內部,用法為:<?php $name = "why"; function changeName(){ global $name; $name = "what";

java中靜態代碼塊的用法—— static用法

super關鍵字 了解 裝載 static關鍵字 super 屬於 註意 lock 自動 (一)java 靜態代碼塊 靜態方法區別一般情況下,如果有些代碼必須在項目啟動的時候就執行的時候,需要使用靜態代碼塊,這種代碼是主動執行的;需要在項目啟動的時候就初始化,在不創建對象的

<!CDATA[]]用法

引號 ica lap 用法 bsp mar ret message eight 所有 XML 文檔中的文本均會被解析器解析。 只有 CDATA 區段(CDATA section)中的文本會被解析器忽略。 PCDATA PCDATA 指的是被解析的字符數據(Parsed

Es6 Promise 用法

set 問題 得到 math clas promise 回調 console spa Promise是什麽?? 打印出來看看 console.dir(Promise) 這麽一看就明白了,Promise是一個構造函數,自己身上有all、reject、r

[轉] angular2-highcharts用法

ppc tip option select sel nbsp 用法詳解 points ttr 1、 使用npm安裝angular2-highcharts npm install angular2-highcharts --save 2、主模塊中引入 app.module.t