進程間通信的方式(四):信號量

進程間通信的方式(四):信號量

來自專欄 RDMA5 人贊了文章

信號量(semaphore)是一種用於提供不同進程之間或者一個給定的不同線程間同步手段的原語。信號量多用於進程間的同步與互斥,簡單的說一下同步和互斥的意思:

同步:處理競爭就是同步,安排進程執行的先後順序就是同步,每個進程都有一定的個先後執行順序。

互斥:互斥訪問不可共享的臨界資源,同時會引發兩個新的控制問題(互斥可以說是特殊的同步)。

競爭:當並發進程競爭使用同一個資源的時候,我們就稱為競爭進程。

共享資源通常分為兩類:一類是互斥共享資源,即任一時刻只允許一個進程訪問該資源;另一類是同步共享資源,即同一時刻允許多個進程訪問該資源;信號量是解決互斥共享資源的同步問題而引入的機制。

1.概述:

簡單說一下信號量的工作機制(因為真的很簡單),可以直接理解成計數器(當然其實加鎖的時候肯定不能這麼簡單,不只只是信號量了),信號量會有初值(>0),每當有進程申請使用信號量,通過一個P操作來對信號量進行-1操作,當計數器減到0的時候就說明沒有資源了,其他進程要想訪問就必須等待(具體怎麼等還有說法,比如忙等待或者睡眠),當該進程執行完這段工作(我們稱之為臨界區)之後,就會執行V操作來對信號量進行+1操作。

臨界區:臨界區指的是一個訪問共用資源(例如:共用設備或是共用存儲器)的程序片段,而這些共用資源又無法同時被多個線程訪問的特性。

臨界資源:只能被一個進程同時使用(不可以多個進程共享),要用到互斥。

我們可以說信號量也是進程間通信的一種方式,比如互斥鎖的簡單實現就是信號量,一個進程使用互斥鎖,並通知(通信)其他想要該互斥鎖的進程,阻止他們的訪問和使用。

當有進程要求使用共享資源時,需要執行以下操作:

1.系統首先要檢測該資源的信號量;

2.若該資源的信號量值大於0,則進程可以使用該資源,此時,進程將該資源的信號量值減1;

3.若該資源的信號量值為0,則進程進入休眠狀態,直到信號量值大於0時進程被喚醒,訪問該資源;

當進程不再使用由一個信號量控制的共享資源時,該信號量值增加1,如果此時有進程處於休眠狀態等待此信號量,則該進程會被喚醒。

2.信號量的具體結構

每個信號量集都有一個與其相對應的結構,該結構定義如下:

/* Data structure describing a set of semaphores. */

struct semid_ds

{

struct ipc_perm sem_perm; /* operation permission struct */

struct sem *sem_base; /* ptr to array of semaphores in set */

unsigned short sem_nsems; /* # of semaphores in set */

time_t sem_otime; /* last-semop() time */

time_t sem_ctime; /* last-change time */

};

/* Data structure describing each of semaphores. */

struct sem

{

unsigned short semval; /* semaphore value, always >= 0 */

pid_t sempid; /* pid for last successful semop(), SETVAL, SETALL */

unsigned short semncnt; /* # processes awaiting semval > curval */

unsigned short semzcnt; /* # processes awaiting semval == 0 */

};

信號量集的結構圖如下所示:

3.代碼實現信號量

使用信號量實現生產者消費者模式

#include "stdafx.h"#include <stdio.h>#include <pthread.h>#include <sched.h>#include <semaphore.h>#include <conio.h>#include <ctype.h>#include <signal.h>#include <iostream>#include<Windows.h>using namespace std;#pragma comment(lib,"pthreadVC2.lib") #define N 5 //消費者或者生產者的數目#define M 10 //緩衝數目int productin = 0; //生產者放置產品的位置int prochaseout = 0; //消費者取產品的位置int buff[M] = {0}; //緩衝區初始化為0,開始時沒有產品。sem_t empty_sem; // 同步信號量,當滿的時候阻止生產者放產品。sem_t full_sem; //同步信號量,當沒有產品的時候阻止消費者消費。pthread_mutex_t mutex; //互斥信號量,一次只有一個線程訪問緩衝區。int product_id = 0; //生產者idint prochase_id = 0; //消費者idvoid SignalExit(int signo){ printf("程序退出%d
",signo); return;}void PrintProduction(){ printf("此時的產品隊列為::"); for(int i = 0; i < M; i++ ) { printf("%d ",buff[i]); } printf("

");}//////////////////////生產者方法////////////////////void* Product(void* pramter){ int id = ++product_id; while(1) { Sleep(5000); //毫秒 sem_wait(&empty_sem); //給信號量減1操作 pthread_mutex_lock(&mutex); productin = productin % M; printf("生產者%d在產品隊列中放入第%d個產品

",id,productin+1); buff[productin] = 1; PrintProduction(); ++productin; pthread_mutex_unlock(&mutex); //釋放互斥量對象 sem_post(&full_sem); //給信號量的值加1操作 }}//////////////消費者方法///////////////////////void* Prochase( void* pramter ){ int id = ++prochase_id; while(1) { Sleep(7000); sem_wait(&full_sem); pthread_mutex_lock(&mutex); prochaseout = prochaseout % M; printf("消費者%d從產品隊列中取出第%d個產品

",id,prochaseout+1); buff[prochaseout] = 0; PrintProduction(); ++prochaseout; pthread_mutex_unlock(&mutex); sem_post(&empty_sem); }}int main(){ cout << "生產者和消費者數目都為5,產品緩衝區為10,生產者每2秒生產一個產品,消費者每5秒消費一個產品" << endl << endl; pthread_t productid[N]; pthread_t prochaseid[N]; int ret[N]; //初始化信號量 int seminit1 = sem_init(&empty_sem,0,M); int seminit2 = sem_init(&full_sem,0,0); if( seminit1 != 0 && seminit2 != 0 ) { printf("sem_init failed !!!
"); return 0; } //初始化互斥信號量 int mutexinit = pthread_mutex_init(&mutex,NULL); if( mutexinit != 0 ) { printf("pthread_mutex_init failed !!
"); return 0; } //創建n個生產者線程 for(int i = 0; i < N; i++ ) { ret[i] = pthread_create( &productid[i], NULL,Product,(void*)(&i) ); if( ret[i] != 0 ) { printf("生產者%d線程創建失敗!
",i); return 0; } } //創建n個消費者線程 for(int j = 0; j < N; j++ ) { ret[j] = pthread_create(&prochaseid[j],NULL,Prochase,NULL); if( ret[j] != 0 ) { printf("消費者%d線程創建失敗
",j); return 0; } } ///////////////////////等待線程被銷毀/////////////////////////////////////////////// for( int k = 0; k < N; k++ ) { printf("銷毀線程
"); pthread_join(productid[k],NULL); pthread_join(prochaseid[k],NULL); } return 0;}

推薦閱讀:

TAG:Linux | 操作系統 | Linux內核 |