1、信號量概念
-
進化版本的互斥鎖,在互斥鎖中,我們用的是兩個線程,而這個信號量是可以N個線程。
-
本質是一個計數器
- 信號量大於0,則信號量- -
- 信號量等於0,造成阻塞
-
既可以在線程中使用,也可以在進程中使用
-
他和管道不同在於,他主要是保護數據,而不是傳輸數據,他是爲了在某一個時刻只有一個線程(或進程)來對數據進行操作。
注意:信號量和信號是兩個不同的關係,要做一個區分。
2、信號量原語
sem_t 建立信號量
sem_init 初始化信號量
sem_wait 阻塞
sem_trywait 嘗試阻塞
sem_timedwait 計時阻塞
sem_post 喚醒信號量
sem_destroy 銷燬信號量
- 頭文件:#include <semaphore.h>
- 返回值:都是成功爲0,失敗位-1
2.1、創建信號量
sem_t類型,用來創建信號量
- 示例:sem_t blank_number這樣就創建了一個 名爲 blank_number的信號量
2.2、初始化信號量
- 函數:int sem_init(sem_t *sem, int pshared, unsigned int value);
- 參數解析:
- 參數1:傳入一個信號量結構體進行初始化
- 參數2:此參數如果爲0表示當前進程的局部信號量,否則,其他進程就能夠共享這個信號量(說白了就是,如果爲0用於線程,不爲零用於進程)
- 參數3:賦值給信號量對象的一個初始值,這個初始值決定作用信號量的線程(進程)個數
- 作用:對給定信號量對象進行初始化。
2.3、信號量喚醒
- 函數:int sem_post(sem_t *sem);
- 參數解析:傳入一個信號量結構體進行喚醒
- 作用:將信號量++,同時喚醒阻塞在信號量的線程,相當於unlock
2.4、信號量阻塞
2.4.1、普通阻塞
函數:int sem_wait(sem_t *sem);
參數解析:傳入一個信號量結構體,來改變對象的值
作用:把信號量- -,如果信號量的值大於零,那麼解析繼續進行,函數立即返回。 如果信號量當前的值爲零,那麼調用就會阻塞,直到可以執行減量操作(例如,信號量值上升到零以上) ,或者信號中斷調用
2.4.2、嘗試阻塞
函數:int sem_trywait(sem_t *sem);
參數解析:傳入一個信號量結構體,來改變對象的值
作用:與 sem_wait ()相同,只是如果不能立即執行遞減操作,則調用返回一個錯誤(errno set to eagain) ,而不是阻塞。
2.4.2、計時阻塞
- 函數:int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
- 參數解析:
- 參數1:傳入一個信號量結構體,來改變對象的值
- 參數2:傳入 在一個timespec結構體來設定等待是時間
- 作用:sem wait ()相同,只是 abs_timeout指定了一個限制,如果不能立即按形式減少調用,則調用應該阻塞的時間量,超過時間返回錯誤。
- 時間結構體
struct timespec
{
time_t tv_sec; /* 秒*/
long tv_nsec; /* 納秒[0 … 999999999] */
};
示例代碼——生產者消費者模型
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
//創建一個可容納5個物體的空間
#define NUM 5
int queue[NUM];
//創建兩個信號變量消費者和生產者
//可以理解成blank_number爲剩餘空間
//product_number爲已佔用的空間
sem_t blank_number, product_number;
//生產者創建物體
void *producer(void *arg)
{
int p = 0;
while (1)
{
//對於生產者來說,生產一個物品,剩餘空間blank_number要減少,已佔用空間product_number要增加
//剩餘空間blank_number減少
sem_wait(&blank_number);
queue[p] = rand() % 1000 + 1;
printf("Produce %d\n", queue[p]);
//佔用空間product_number增加
sem_post(&product_number);
//p取模的話就能把p限制在1-5一直循環
p = (p+1)%NUM;
//隨機睡眠
sleep(rand()%5);
}
}
//消費者消費物體
void *consumer(void *arg)
{
//對於消費者來說,剩餘空間要增加,佔用空間要減少,因爲我這個消費者消費了一個物品
int c = 0;
while (1)
{
//已佔用空間product_number減少
sem_wait(&product_number);
printf("Consume %d\n", queue[c]);
queue[c] = 0;
//剩餘空間blank_number增加
sem_post(&blank_number);
c = (c+1)%NUM;
sleep(rand()%5);
}
}
int main(void)
{
pthread_t pid, cid;
//初始化兩個信號量
sem_init(&blank_number, 0, NUM);
sem_init(&product_number, 0, 0);
//線程創建
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
//線程回收
pthread_join(pid, NULL);
pthread_join(cid, NULL);
//信號量銷燬
sem_destroy(&blank_number);
sem_destroy(&product_number); return 0;
}