天天看點

Linux多程序機制和鎖(四)信号量

我們在面試的時候經常會被問到,程序間的方式有幾種分别應用于哪些場景?其中有一項最容易答漏的便是信号量

信号量不僅可以用來完成程序間的通信,還可以用來進行線程中的通信

信号量用在多線程多任務同步的,一個線程完成了某一個動作就通過信号量告訴别的線程,别的線程再進行某些動作。信号量不一定是鎖定某一個資源,而是流程上的概念,比如:有 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);
        }


}

           
Linux多程式機制和鎖(四)信号量

運作結果如下:

Linux多程式機制和鎖(四)信号量

細心的同學會發現,執行順序都是生産者生産完了之後消費者立即消費,随即生産者繼續生産,沒有起到上個程式的作用

為什麼呢?我們的代碼中将資源數目設為了1,這樣消費完了一個資源以後當然是生産啦,生産完就消費,順序交替。

Linux多程式機制和鎖(四)信号量
//函數的聲明如下
int sem_init(sem_t *sem, int pshared, unsigned int value);
//sem代表被初始化的信号量
//pshare :指明信号量的類型,0用于線程間的通信,非0用于程序間的通信
//value為資源數,也就是所謂信号燈的盞數

           

是以我們将資源數改成5就可以5個生産者同時生産和5個消費者同時消費了

将資源數改成5之後運作結果如下:

Linux多程式機制和鎖(四)信号量

我們可以看到生産者和消費者可以同時消費和生産了,但是發生了一個段錯誤,這是為什麼呢?

多個消費者同時消費,當一個資源正在被消費的時候,另一個消費者就去消費它,就會造成沖突,是以就發生了異常

是以我們應該使用互斥鎖來制約線程間的執行,

如果我們這樣加鎖會導緻一個問題

Linux多程式機制和鎖(四)信号量

結果死鎖:

Linux多程式機制和鎖(四)信号量

是因為我們在拿到互斥鎖的時候如果沒有資源就會一直拿着鎖不放,造成死鎖的情況發生,是以我們首先要判斷資源是否有,之後再加鎖

Linux多程式機制和鎖(四)信号量

這樣加鎖就不會導緻死鎖了

繼續閱讀