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


}

在这里插入图片描述
运行结果如下:
在这里插入图片描述
细心的同学会发现,执行顺序都是生产者生产完了之后消费者立即消费,随即生产者继续生产,没有起到上个程序的作用
为什么呢?我们的代码中将资源数目设为了1,这样消费完了一个资源以后当然是生产啦,生产完就消费,顺序交替。

在这里插入图片描述

//函数的声明如下
int sem_init(sem_t *sem, int pshared, unsigned int value);
//sem代表被初始化的信号量
//pshare :指明信号量的类型,0用于线程间的通信,非0用于进程间的通信
//value为资源数,也就是所谓信号灯的盏数

所以我们将资源数改成5就可以5个生产者同时生产和5个消费者同时消费了
将资源数改成5之后运行结果如下:
在这里插入图片描述
我们可以看到生产者和消费者可以同时消费和生产了,但是发生了一个段错误,这是为什么呢?
多个消费者同时消费,当一个资源正在被消费的时候,另一个消费者就去消费它,就会造成冲突,所以就发生了异常
所以我们应该使用互斥锁来制约线程间的执行,
如果我们这样加锁会导致一个问题
在这里插入图片描述
结果死锁:
在这里插入图片描述
是因为我们在拿到互斥锁的时候如果没有资源就会一直拿着锁不放,造成死锁的情况发生,所以我们首先要判断资源是否有,之后再加锁
在这里插入图片描述
这样加锁就不会导致死锁了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值