Linux基礎——線程同步之信號量

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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章