我们在面试的时候经常会被问到,进程间的方式有几种分别应用于哪些场景?其中有一项最容易答漏的便是信号量
信号量不仅可以用来完成进程间的通信,还可以用来进行线程中的通信
信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。信号量不一定是锁定某一个资源,而是流程上的概念,比如:有 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之后运行结果如下:
我们可以看到生产者和消费者可以同时消费和生产了,但是发生了一个段错误,这是为什么呢?
多个消费者同时消费,当一个资源正在被消费的时候,另一个消费者就去消费它,就会造成冲突,所以就发生了异常
所以我们应该使用互斥锁来制约线程间的执行,
如果我们这样加锁会导致一个问题
结果死锁:
是因为我们在拿到互斥锁的时候如果没有资源就会一直拿着锁不放,造成死锁的情况发生,所以我们首先要判断资源是否有,之后再加锁
这样加锁就不会导致死锁了