在前面的进程控制里面我们说到了System V版本的信号量,主要用于进程控制。同样,线程控制里面也有相应的信号量,它用于线程间同步,就是我们下面要讲的Posix信号量
说到信号量,我们都明白,信号量就相当于一个计数器,它的目的是为了保护临界资源,通过信号量的P,V操作,就可以对临界资源的数目进行修改
POSIX信号量
初始化信号量
#include<semaphore.h>
int sem_init(seq_t *sem,int pshared,unsifned int value);
参数:
pshared:0表示线程间共享,非零表示进程间共享
value:信号量的初始值
销毁信号量
int sem_destroy(sem_t *sem);
等待信号量
功能:等待信号量, 将信号量的值减一
int sem_wait(sem_t *sem);
发布信号量
功能:发布信号量,表示资源使用完毕,可以归还资源了,将信号量值加一
int sem_post(sem_t *sem);
接下来我们通过一个具体的例子了解信号量的作用
消费者与生产者模型
消费者和生产者我们都知道
生产者生产后放到仓库,消费者拿去消费
那么问题来了!!!
当生产者生产的慢,消费者比较多的时候,就会出现,消费者一直申请锁释放锁,线程死锁问题,
如果是消费者慢,生产者快,那么在一个环状结构中,生产者生产的新的数据就会把之前的数据覆盖掉产生严重的后果
下面先看一个没有信号量的
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define CONSUMERS_COUNT 2
#define PRODUCERS_COUNT 2
struct msg{
struct msg *next;
int num;
};
struct msg *head = NULL;
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_t threads[CONSUMERS_COUNT + PRODUCERS_COUNT];
void *consumer(void *p)
{
int num = *(int *)p;
free(p);
struct msg *mp;
for(;;){
pthread_mutex_lock(&mutex);
while(head == NULL)
{
printf("%d begin wait a condition...\n",num);
pthread_cond_wait(&cond,&mutex);
}
printf("%d end wait a condition...\n",num);
printf("%d begin consume product...\n",num);
mp = head;
head = mp->next;
pthread_mutex_unlock(&mutex);
printf("consume %d\n",mp->num);
free(mp);
printf("%d end consumer product...\n",num);
sleep(rand()%5);
}
}
void *producer(void *p)
{
struct msg *mp;
int num = *(int *)p;
free(p);
for(;;)
{
printf("%d begin product product...\n",num);
mp = (struct msg*)malloc(sizeof(struct msg));
mp ->num = rand()%1000 + 1;
printf("produce %d\n",mp->num);
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
printf("%d end produce product...\n",num);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
sleep(rand()%5);
}
}
int main(void)
{
srand(time(NULL));
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&mutex,NULL);
int i;
for(i = 0;i < CONSUMERS_COUNT;i++)
{
int *p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&threads[i],NULL,consumer,(void*)p);
}
for(i = 0;i<PRODUCERS_COUNT;i++)
{
int *p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&threads[CONSUMERS_COUNT+1],NULL,producer,(void*)p);
}
for(i = 0;i < CONSUMERS_COUNT+PRODUCERS_COUNT;i++)
{
pthread_join(threads[i],NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
我们可以看到生产者与消费者之间会出现问题
所以我们在环状的生产者与消费者之间加上信号量来看看
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <semaphore.h>
#include <errno.h>
#include <string.h>
//#define ERR_EXIT(m);
//do
//{
// perror(m);
// exit(EXIT_FALLURE);
//}while(0)
#define CONSUMERS_COUNT 1
#define PRODUCERS_COUNT 1
#define BUFFSIZE 10
int g_buffer[BUFFSIZE];
unsigned short in = 0;
unsigned short out = 0;
unsigned short produce_id = 0;
unsigned short consume_id = 0;
sem_t g_sem_full;
sem_t g_sem_empty;
pthread_mutex_t g_mutex;
pthread_t g_threads[CONSUMERS_COUNT + PRODUCERS_COUNT];
void *consumer(void *p)
{
int i;
int num = *(int *)p;
free(p);
while(1){
printf("%d wait buffer not empty\n",num);
sem_wait(&g_sem_empty);
pthread_mutex_lock(&g_mutex);
for(i = 0;i < BUFFSIZE;i++)
{
printf("%2d",i);
if(g_buffer[i] == -1)
{
printf("%s","null");
}
else
{
printf("%d",g_buffer[i]);
}
if(i == out)
{
printf("\t < --consume");
}
printf("\n");
}
consume_id = g_buffer[out];
printf("%d begin consume product %d\n",num,consume_id);
g_buffer[out] = -1;
out = (out + 1) % BUFFSIZE;
printf("%d end consume product %d\n",num,consume_id);
pthread_mutex_unlock(&g_mutex);
sem_post(&g_sem_full);
sleep(1);
}
return NULL;
}
void *producer(void *p)
{
int i;
int num = *(int *)p;
free(p);
while(1)
{
printf("%d wait buffer not full\n",num);
sem_wait(&g_sem_full);
pthread_mutex_lock(&g_mutex);
for(i = 0;i < BUFFSIZE;i++)
{
printf("%2d",i);
if(g_buffer[i] == -1)
{
printf("%s","null");
}
else
{
printf("%d",g_buffer[i]);
}
if(i == in)
{
printf("\t < --produce");
}
printf("\n");
}
printf("%d begin produce product %d\n",num,produce_id);
g_buffer[in] = produce_id;
in = (in +1) % BUFFSIZE;
printf("%d end produce product %d",num,produce_id);
pthread_mutex_unlock(&g_mutex);
sem_post(&g_sem_empty);
sleep(5);
}
return NULL;
}
int main(void)
{
int i;
for(i = 0; i < BUFFSIZE;i++)
{
g_buffer[i] = -1;
}
sem_init(&g_sem_full,0,BUFFSIZE);
sem_init(&g_sem_empty,0,BUFFSIZE);
pthread_mutex_init(&g_mutex,NULL);
//srand(time(NULL));
//pthread_cond_init(&cond,NULL);
//pthread_mutex_init(&mutex,NULL);
//int i;
for(i = 0;i < CONSUMERS_COUNT;i++)
{
int *p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&g_threads[i],NULL,consumer,(void*)p);
}
for(i = 0;i<PRODUCERS_COUNT;i++)
{
int *p = (int *)malloc(sizeof(int));
*p = i;
pthread_create(&g_threads[CONSUMERS_COUNT+1],NULL,producer,(void*)p);
}
for(i = 0;i < CONSUMERS_COUNT+PRODUCERS_COUNT;i++)
{
pthread_join(g_threads[i],NULL);
}
sem_destroy(&g_sem_full);
sem_destroy(&g_sem_empty);
pthread_mutex_destroy(&g_mutex);
return 0;
}
由此我们可以看出,生产者生产出来的东西不会因为,消费者快而产生死锁问题,也不会因为生产者快而产生数据覆盖问题
信号量较好的解决了出现的问题