三个概念:生产者消费者模型、线程、信号量
1、生产者消费者模型
利用生活中的例子,生产者生产商品,商品放在超市,消费者去超市购买(使用)商品,也就是生产者消费者模型。
生产者生成一定的数据放在缓冲区,然后重复此过程,与此同时,消费者也在缓冲区使用这些数据。
问题聚焦到缓冲区,既然使用同样的缓冲区,就引出了几个问题:
a.缓冲区的先后访问顺序问题
b.在缓冲区要满时,生产者不会在缓冲区写入数据
c.在缓冲区要空时,消费者不会在缓冲区读取数据
2、线程
要实现生产者消费者模型的问题,因为生产者和消费者需要同时访问同一块缓冲区,同一块内存,就需要使用到线程。
同一个进程中的多个线程共享进程的地址空间,同个线程共享同一个全局变量,各个线程统一参与CPU的调度,线程间的执行顺序不确定。
多个线程必须按照事先约定好的先后顺序访问全局变量,这种先后次序依赖于要解决某种问题,生产者和消费者之间就需要解决同步问题,生产者需要先生产,消费者才能消费,linux系统提供信号量实现同步问题。
同一个时刻只能有一个线程访问资源,写入或者读取,且在缓冲区满时,生产者不会继续在缓冲区写入数据,在缓冲区空时,消费者不会继续从缓冲区读取数据,解决临界资源不被破坏,就是互斥问题,为了实现互斥,就需要互斥锁。
3、信号量
a.信号量代表了某一类资源,其值表示系统中该资源的数量
b.信号量是一个受保护的量,只能通过三种操作函数来访问
sem_init() //初始化信号量
sem_wait() //P操作(申请资源)
sem_post() //V操作(释放资源)
c.信号量的值为非负整数
4、互斥锁
互斥锁操作函数: pthread_mutex_init() //初始化
pthread_mutex_lock() //加锁
pthread_mutex_unlock()//解锁
初始化完互斥锁之后,每个线程在访问资源时都先尝试加锁,加锁成功就可以访问资源,使用完资源之后解锁。
代码段:一个生产者,一个消费者
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#define SIZE 5
int buff[SIZE] = {0};
sem_t sem_r;//保存可读信号量的变量,代表buf可读资源数
sem_t sem_w;//保存可写信号量的变量,代表buf可写资源数
void* producer(void* arg)//子线程
{
while(1)
{
sleep(1);
sem_wait(&sem_w);
int i = 0;
int data = rand()%100;
buff[i] = data;
++i;
i %= 5;
sem_post(&sem_r);
printf("produce:%d\n",data);
}
}
void* consumer(void* arg)
{
while(1)
{
sleep(1);
sem_wait(&sem_r);
int i = 0;
int data = buff[i];
++i;
i %= 5;
sem_post(&sem_w);
printf("consumer:%d\n",data);
}
}
int main(int argc, char *argv[])
{
srand(time(NULL));
pthread_t p,c;//保存线程号
if(sem_init(&sem_r,0,0) == -1)//初始化信号量
{
perror("sem_init");
return -1;
}
if(sem_init(&sem_w,0,1) == -1)//初始化信号量
{
perror("sem_init");
return -1;
}
if(0 != pthread_create(&p,NULL,producer,NULL))//创建子线程
{
perror("pthread_create");
return -1;
}
if(0 != pthread_create(&c,NULL,consumer,NULL))//创建子线程
{
perror("pthread_create");
return -1;
}
if(0 != pthread_join(p,NULL))//非阻塞等待回收子线程产生的资源
{
perror("pthread_join");
return -1;
}
if(0 != pthread_join(c,NULL))//非阻塞等待回收子线程产生的资源
{
perror("pthread_join");
return -1;
}
return 0;
}
代码段:两个生产者,两个消费者
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#define SIZE 5
int buff[SIZE] = {0};
sem_t sem_r;//保存可读信号量的变量,代表buf可读资源数
sem_t sem_w;//保存可写信号量的变量,代表buf可写资源数
pthread_mutex_t mutex_p;
pthread_mutex_t mutex_c;
void* producer(void* arg)//子线程
{
while(1)
{
sleep(1);
sem_wait(&sem_w);
pthread_mutex_lock(&mutex_p);
int i = 0;
int data = rand()%100;
buff[i] = data;
++i;
i %= 5;
pthread_mutex_unlock(&mutex_p);
sem_post(&sem_r);
printf("produce:%d\n",data);
}
}
void* consumer(void* arg)
{
while(1)
{
sleep(1);
sem_wait(&sem_r);
pthread_mutex_lock(&mutex_c);
int i = 0;
int data = buff[i];
++i;
i %= 5;
pthread_mutex_unlock(&mutex_c);
sem_post(&sem_w);
printf("consumer:%d\n",data);
}
}
int main(int argc, char *argv[])
{
srand(time(NULL));
pthread_t p1,p2,c1,c2;//保存线程号
if(sem_init(&sem_r,0,0) == -1)//初始化信号量
{
perror("sem_init");
return -1;
}
if(sem_init(&sem_w,0,1) == -1)//初始化信号量
{
perror("sem_init");
return -1;
}
if(0 != pthread_create(&p1,NULL,producer,NULL))//创建子线程
{
perror("pthread_create");
return -1;
}
if(0 != pthread_create(&p2,NULL,producer,NULL))//创建子线程
{
perror("pthread_create");
return -1;
}
if(0 != pthread_create(&c1,NULL,consumer,NULL))//创建子线程
{
perror("pthread_create");
return -1;
}
if(0 != pthread_create(&c2,NULL,consumer,NULL))//创建子线程
{
perror("pthread_create");
return -1;
}
if(0 != pthread_join(p1,NULL))//非阻塞等待回收子线程产生的资源
{
perror("pthread_join");
return -1;
}
if(0 != pthread_join(p2,NULL))//非阻塞等待回收子线程产生的资源
{
perror("pthread_join");
return -1;
}
if(0 != pthread_join(c1,NULL))//非阻塞等待回收子线程产生的资源
{
perror("pthread_join");
return -1;
}
if(0 != pthread_join(c2,NULL))//非阻塞等待回收子线程产生的资源
{
perror("pthread_join");
return -1;
}
return 0;
}