我們在面試的時候經常會被問到,程序間的方式有幾種分别應用于哪些場景?其中有一項最容易答漏的便是信号量
信号量不僅可以用來完成程序間的通信,還可以用來進行線程中的通信
信号量用在多線程多任務同步的,一個線程完成了某一個動作就通過信号量告訴别的線程,别的線程再進行某些動作。信号量不一定是鎖定某一個資源,而是流程上的概念,比如:有 A,B 兩個線程,B 線程要等 A 線程完成某一任務以後再進行自己下面的步驟,這個任務并不一定是鎖定某一資源,還可以是進行一些計算或者資料處理之類。
信号量(信号燈)與互斥鎖和條件變量的主要不同在于” 燈” 的概念,燈亮則意味着資源可用,燈滅則意味着不可用。信号量主要阻塞線程,不能完全保證線程安全,如果要保證線程安全,需要信号量和互斥鎖一起使用。
就上一章節的例子而言,我們是使用條件變量來實作的,現在我們用信号量來達到同樣的效果,引進信号量之後我們的代碼會相對減少變得簡單一點
我們直接将代碼貼出來再詳細分析:
#include<stdio.h>
#include<pthread.h>
#include <stdlib.h>
#include<semaphore.h>
pthread_mutex_t mutex;
pthread_cond_t my_cond;
sem_t p_sem;
sem_t c_sem;
typedef struct Node
{
int num;
struct Node *next;
}node;
node *head=NULL;
void *produce(void *argv);
void *consumer(void *argv);
int main(void)
{
pthread_mutex_init(&mutex,NULL);
sem_init(&p_sem,0,1);
sem_init(&c_sem,0,0);
pthread_t pro[5],con[5];
for(int i=0;i<5;i++)
{
pthread_create(&pro[i],NULL,produce,NULL);
pthread_create(&con[i],NULL,consumer,NULL);
}
for(int i=0;i<5;i++)
{
pthread_join(pro[i],NULL);
pthread_join(con[i],NULL);
}
sem_destroy(&p_sem);
sem_destroy(&c_sem);
pthread_mutex_destroy(&mutex);
return 0;
}
void *produce(void *argv)
{
while(1)
{
//pthread_mutex_lock(&mutex);
sem_wait(&p_sem);
node * n=(node*)malloc(sizeof(struct Node));
n->num=rand()%10;
n->next=head;
head=n;
printf("produce %ld, num =%d\n",pthread_self(),n->num);
sem_post(&c_sem);
//pthread_mutex_unlock(&mutex);
sleep(rand()%5);
}
return NULL;
}
void *consumer(void *argv)
{
while(1)
{
//pthread_mutex_lock(&mutex);
//while(head==NULL)
//{
// pthread_cond_wait(&my_cond,&mutex);
//}
sem_wait(&c_sem);
node * my_node=head;
printf("consumer id=%ld ,num=%d\n",pthread_self(),my_node->num);
head=head->next;
free(my_node);
sem_post(&p_sem);
//pthread_mutex_unlock(&mutex);
sleep(rand()%3);
}
}
運作結果如下:
細心的同學會發現,執行順序都是生産者生産完了之後消費者立即消費,随即生産者繼續生産,沒有起到上個程式的作用
為什麼呢?我們的代碼中将資源數目設為了1,這樣消費完了一個資源以後當然是生産啦,生産完就消費,順序交替。
//函數的聲明如下
int sem_init(sem_t *sem, int pshared, unsigned int value);
//sem代表被初始化的信号量
//pshare :指明信号量的類型,0用于線程間的通信,非0用于程序間的通信
//value為資源數,也就是所謂信号燈的盞數
是以我們将資源數改成5就可以5個生産者同時生産和5個消費者同時消費了
将資源數改成5之後運作結果如下:
我們可以看到生産者和消費者可以同時消費和生産了,但是發生了一個段錯誤,這是為什麼呢?
多個消費者同時消費,當一個資源正在被消費的時候,另一個消費者就去消費它,就會造成沖突,是以就發生了異常
是以我們應該使用互斥鎖來制約線程間的執行,
如果我們這樣加鎖會導緻一個問題
結果死鎖:
是因為我們在拿到互斥鎖的時候如果沒有資源就會一直拿着鎖不放,造成死鎖的情況發生,是以我們首先要判斷資源是否有,之後再加鎖
這樣加鎖就不會導緻死鎖了