信號量、互斥鎖和條件變量的區別


一、

1、互斥鎖總是必須由給其上鎖的線程解鎖,信號量的掛出確不必由執行過它的等待操作的同一線程執行。

                                生產者與消費者偽代碼

 

2、互斥鎖要么被鎖住,要么被解鎖(二值狀態,類似於二值信號量)

3、既然信號量有一個與之關聯的狀態(它的數值),那么信號量的掛出操作總是被記住。然而當向一個條件變量發送信號時,如果沒有線程等待在該條件變量上,那么信號將丟失。

 

 

 

ps:提供信號量的原因是,在進程間同步的情況下,若沒有涉及到共享內存區時,需要使用信號量。

 

二、

  1、posix提供量中信號量:有名信號量和基於內存的信號量,后者被稱為無名信號量。

  

有名信號量如下圖所示:

  

內存信號量(無名信號量)如下圖所示:

    

三、有名信號量

  sem_open:創建一個新的有名信號量或者打開一個已經存在的有名信號量。有名信號量既可用於線程間同步,也可用於進程間同步。

頭文件:#include <semaphore.h>
函數原型:sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value);
參數:
name 信號量的外部名字
oflag 選擇創建或打開一個現有的信號量
mode 權限位
value 信號量初始值
oflag參數可以是0、O_CREAT(創建一個信號量)或O_CREAT|O_EXCL(如果沒有指定的信號量就創建),如果指定了O_CREAT,那么第三個和第四個參數是需要的;其中mode參數指定權限位,value參數指定信號量的初始值,通常用來指定共享資源的書面。該初始不能超過SEM_VALUE_MAX,這個常值必須低於為32767。二值信號量的初始值通常為1,計數信號量的初始值則往往大於1。
 
         
如果指定了O_CREAT(而沒有指定O_EXCL),那么只有所需的信號量尚未存在時才初始化它。所需信號量已存在條件下指定O_CREAT不是一個錯誤。該標志的意思僅僅是“如果所需信號量尚未存在,那就創建並初始化它”。但是所需信號量等已存在條件下指定O_CREAT|O_EXCL卻是一個錯誤。
 
         
sem_open返回指向sem_t信號量的指針,該結構里記錄着當前共享資源的數目。
 

sem_close用於關閉打開着的信號量:

#include <semaphore.h>

int sem_close(sem_t *sem)

sem_unlink將有名信號量從系統中刪除

#include <semaphore.h>

int sem_unlink(const char *name)
sem_wait是一個函數,也是一個 原子操作,它的作用是從 信號量的值減去一個“1”,但它永遠會先等待該信號量為一個非零值才開始做減法。也就是說,如果你對一個值為2的信號量調用sem_wait(), 線程將會繼續執行,將信號量的值將減到1。
如果對一個值為0的信號量調用sem_wait(),這個函數就會原地等待直到有其它線程增加了這個值使它不再是0為止。如果有兩個線程都在sem_wait()中等待同一個信號量變成非零值,那么當它被第三個 線程增加 一個“1”時,等待線程中只有一個能夠對信號量做減法並繼續執行,另一個還將處於等待狀態。sem_trywait(sem_t *sem)是函數sem_wait的非阻塞版,它直接將信號量sem減1,同時返回錯誤代碼EAGAIN。
#include <semaphore.h>

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);

sem_post是給信號量的值加上一個“1”,它是一個“原子操作”---即同時對同一個信號量做加“1”操作的兩個線程是不會沖突的;

#include <semaphore.h>
int sem_post(sem_t *sem);

sem_getvalue,計算機術語,是把 sem 指向的信號量當前值放置在 sval 指向的整數上。 如果有一個或多個進程或線程當前正在使用 sem_wait(3) 等待信號量,POSIX.1-2001 允許返回兩種結果在 sval 里:要么返回 0;要么返回一個負值,它的絕對等於當前正在 sem_wait(3) 里阻塞的進程和線程數。Linux 選擇了前面的行為(返回零)。

#include <semaphore.h>

int sem_getvalue(sem_t *sem,int *sval);

posix有名信號量至少是隨內核持續性的,因此可以跨多個進程操作他們。

 

 四、無名信號量

 之前的內容處理的是posix有名信號量的內容,這些信號量是由一個name參數標識的,它通常指代文件系統中的某個文件。然而posix也提供了基於內存的信號量。

 

int sem_init(sem_t *sem, int pshared, unsigned int value);
如果 pshared 是0,那么初始化的信號量是在同一個進程的各個線程間共享的。
如果 pshared 是非零值,那么信號量將在進程之間共享,並且應該定位共享內存區域(見 shm_open(3)、mmap(2) 和 shmget(2))。因為通過 fork(2) 
創建的孩子繼承其父親的內存映射,因此它也可以見到這個信號量。所有可以訪問共享內存區域的進程都可以用 sem_post(3)、sem_wait(3) 等等操作信號量。
初始化一個已經初始的信號量其結果未定義。 參數 sem :指向信號量對象   pshared : 指明信號量的類型。不為0時此信號量在進程間共享,否則只能為當前進程的所有線程共享。   value : 指定信號量值的大小 返回值 sem_init() 成功時返回
0;錯誤時,返回 -1,並把 errno 設置為合適的值。 錯誤 EINVAL value 超過 SEM_VALUE_MAX。 ENOSYS pshared 非零,但系統還沒有支持進程共享的信號量。

摧毀信號量:

#include <semaphore.h>

int sem_destroy(sem_t *sem);

 

  進程間共享基於內存信號量的規則:信號量本身(其作為sem_init第一個參數的sem_t數據類型變量)必須駐留在由所有希望共享它的進程所共享的內存區中,而且sem_init的第二個參數必須是1。

  在父進程中打開的任何信號量仍應在子進程中打開。如下:

  

sem_t *sem;

sem = sem_open("sem name",O_CREATE|O_EXCL,FILE_MODE,0);
if(childpid = fork() == 0)
{
        sem_wait(sem);      
}
else
{
        sem_post(sem);  
}

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM