一.基础知识

1.1 system V版本的信号量和POSIX下信号量的区别

   我们前面讲过进程间通信有三种形式:管道、消息队列、信号量。这个信号量是system V版本下,以信号量集形式申请存在的,它标识着一个临界资源的有无从而控制不同的进程能否访问到该临界资源。但,现在,我们要讲的信号量是基于POSIX下的信号量,它用来标识资源的个数。


1.2 互斥锁和信号量

       上篇所述,互斥锁(Mutex)可看作是某种资源的可用数,Mutex变量是非0即1的,初始化时Mutex为1,表示有一个可用资源;加锁时获得该资源,将Mutex减到0,表示不再有可用资源;解锁时释放该资源,将Mutex重新加到1,表示又有了一个可用资源。

       即,信号量和Mutex作用类似,表示可用资源的数量,和Mutex不同的是信号量的数量可以大于1。

如果信号量描述的资源数目是1时,此时的信号量就和互斥锁相同!

   这种信号量不仅可用于同一进程的线程间同步,也可用于不同进程间的同步。


二.相关函数

2.1初始化

wKioL1cclRfwj8OhAAAeIbLxH5E257.png

semaphore变量的类型为sem_t,sem_init()初始化一个semaphore变量,

pshared:参数为0表示信号量用于同一进程的线程间同步

value:可用资源的数量

2.2 释放资源

wKioL1cco_yBeoz2AAAZ6bdz1gU503.png

2.3 P、V操作

wKiom1ccok_A1kosAAAoFPKITfc371.png

sem_wait():获得资源(P操作-1),使semaphore的值减1,如果调用sem_wait()时semaphore的值已经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait() 。

sem_post():释放资源(V操作+1),使semaphore 的值加1,同时唤醒挂起等待的线程。


三. 相关代码(多生产者多消费者模型)

//test.c  
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 #include<semaphore.h>
  5 
  6 #define MAX 20
  7 static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
  8 sem_t blank;
  9 sem_t data;
 10 
 11 int buf[MAX];
 12 
 13 void* product(void* arg)
 14 {
 15     int i=0;
 16     int index=0;
 17     while(1)
 18     {
 19         sem_wait(&blank);
 20         pthread_mutex_lock(&lock);
 21         buf[index++]=i++;
 22         index%=MAX;
 23         printf("product%d done...val is :%d\n",(int)arg,i-1);
 24         sem_post(&data);
 25         pthread_mutex_unlock(&lock);
 26         sleep(2);
 27     }
 28 }
 29 void* consumer(void* arg)
 30 {
 31     int index=0;
 32     while(1)
 33     {
 34         sem_wait(&data);
 35         pthread_mutex_lock(&lock);
 36         int val=buf[index++];
 37         index%=MAX;
 38         printf("consumer%d done...val is:%d\n",(int)arg,val);
 39         sem_post(&blank);
 40         pthread_mutex_unlock(&lock);
 41         sleep(1);
 42     }
 43 }
 44 int main()
 45 {
 46     sem_init(&blank,0,MAX);
 47     sem_init(&data,0,0);
 48 
 49     pthread_t product1;
 50     pthread_t product2;
 51     pthread_t product3;
 52     pthread_t consumer1;
 53     pthread_t consumer2;
 54     pthread_t consumer3;
 55 
 56     pthread_create(&product1,NULL,product,(void*)1);
 57     pthread_create(&product2,NULL,product,(void*)2);
 58     pthread_create(&product3,NULL,product,(void*)3);
 59     pthread_create(&consumer1,NULL,consumer,(void*)1);
 60     pthread_create(&consumer2,NULL,consumer,(void*)2);
 61     pthread_create(&consumer3,NULL,consumer,(void*)3);
 62 
 63     pthread_join(product1,NULL);
 64     pthread_join(product2,NULL);
 65     pthread_join(product3,NULL);
 66     pthread_join(consumer1,NULL);
 67     pthread_join(consumer2,NULL);
 68     pthread_join(consumer3,NULL);
 69 
 70     sem_destroy(&blank);
 71     sem_destroy(&data);
 72 
 73     pthread_mutex_destroy(&lock);
 74     
 75     return 0;
 76 }
 
 //makefile
   1 test:test.c
  2     gcc -o $@ $^ -lpthread
  3 .PHONY:clean
  4 clean:
  5     rm -f test

输出结果:

wKioL1ccuwPRWU6xAABCPRQ6p2E452.png


四.分析与总结

    1)单生产但消费者模型,因为P、V操作已经让两者有序,相当于完成了同步与互斥(上述代码实现中去掉加锁解锁即第20,25,35,40行);多生产者多消费者模型,即上述代码实现。(因为消费者和消费者之间存在互斥问题,生产者和生产者之间也存在互斥问题)

    2)加锁解锁的方式还有一种,即加在sem_wait之前,但这样可能会造成死锁问题:若消费者先进入,申请data,而此时产生data的生产者被挂起等待。