Linux多线程是Linux型程序设计中十分重要概念,其中线程同步就更加重要了,生产者与消费者模型就是线程同步的经典例子,下面我将给你三种多线程实现生产者和消费者的例子。
一、生产者与消费者逻辑
关键的问题,在于生产者和消费者什么时候睡眠,又什么时候被唤醒,这就是生产者和消费者模型的关键。
缓冲区模型:
使用唤醒缓冲区,方向为逆时针。
read表示已经读取的位置
write表示即将写入的位置
缓冲区初始状态:
read不等于write。
生产者写满:
write==read,即将写的地方为刚读取的位置,就是下一个位置还没有读,所以代表写满。
消费者读完:
read+1=write,即将读的地方为即将写入得位置,还没有写,就不能再读了,这里就代表读完。
二、信号量模型
#include
#include
#include
#include
#include
//临界资源描述信息
struct Data
{
int d[5] ; //环形缓冲区
int ptr_read; //已经读位置
int ptr_write; //即将写位置
sem_t can_read; //可读数据数目
sem_t can_write; //可写数据数目
};
struct Data data;
int length=5;
static int value=0;//作为环形缓冲区的数据
void init_data(void)
{
data.ptr_read=4;
data.ptr_write=0;
sem_init(&data.can_read,0,0);
sem_init(&data.can_write,0,length);
}
void *thread_consumer(void *arg);
void *thread_producter(void *arg);
int main()
{
init_data();
/************创建消费者和生产者线程*******************/
pthread_t consumer,producter;
pthread_attr_t attr;
void *thread_result;
/***设置线程为脱离类型***/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
/***创建消费者线程***/
pthread_create(&consumer, &attr, thread_consumer, NULL);
/***创建生产者线程***/
pthread_create(&producter, &attr, thread_producter, NULL);
while(1)
{
sleep(2);
}
}
void *thread_consumer(void *arg)
{
int count=0;
while(1)
{
/****随机的消费数据使得问题适应性更广*****/
int sleep_time=rand()%3;
sleep(sleep_time);
sem_wait(&data.can_read);//减少可读信号量
printf("sub data is = %d \n",data.d[(data.ptr_read+1)%length]);//读出数据
data.ptr_read=(data.ptr_read+1)%length;//更新已读位置
sem_post(&data.can_write);//增加可写信号量
}
}
void *thread_producter(void *arg)
{
int count=0;
while(1)
{
int sleep_time=rand()%3;
sleep(sleep_time);
int ret=sem_wait(&data.can_write);//减少可写信号量
data.d[data.ptr_write]=++value; //写入新的值
printf("------add data is = %d \n",data.d[data.ptr_write]);//打印新生产的值
data.ptr_write=(data.ptr_write+1)%length;//更新即将写入的位置
ret=sem_post(&data.can_read);//增加可读信号量
}
}
三、条件变量模型
#include
#include
#include
#include
#include
//临界资源描述信息
struct Data
{
int d[5] ; //临界资源
int ptr_read; //已经读指针位置
int ptr_write; //即将写指针位置
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_cond_t can_read;
pthread_cond_t can_write;
};
static int value=0;
struct Data data;
int length=5;
void init_data(void)
{
data.ptr_read=4;
data.ptr_write=0;
pthread_cond_init(&data.can_read,NULL);
pthread_mutex_init(&data.mutex1,NULL);
pthread_cond_init(&data.can_write,NULL);
pthread_mutex_init(&data.mutex2,NULL);
}
void *thread_consumer(void *arg);
void *thread_producter(void *arg);
int main()
{
init_data();
/************创建消费者和生产者线程*******************/
pthread_t consumer,producter;
pthread_attr_t attr;
void *thread_result;
/***设置线程为脱离类型***/
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
/***创建消费者线程***/
pthread_create(&consumer, &attr, thread_consumer, NULL);
/***创建生产者线程***/
pthread_create(&producter, &attr, thread_producter, NULL);
while(1)
{
sleep(2);
}
}
void *thread_consumer(void *arg)
{
sleep(3);//消费者先睡眠2s,让生产者生产数据
while(1)
{
/****随机的消费数据使得问题适应性更广*****/
int sleep_time=rand()%3;
sleep(sleep_time);
pthread_mutex_lock(&data.mutex1);
if((data.ptr_read+1)%length==data.ptr_write) //即将读的位置还没有写好数据
pthread_cond_wait(&data.can_read,&data.mutex1);
printf("sub data:%d\n",data.d[(data.ptr_read+1)%length]);//唤醒后即可读取数据
data.ptr_read=(data.ptr_read+1)%length; //更新已经读取指针位置
pthread_cond_signal(&data.can_write);
pthread_mutex_unlock(&data.mutex1);
}
}
void *thread_producter(void *arg)
{
int count=0;
while(1)
{
int sleep_time=rand()%3;
sleep(sleep_time);
pthread_mutex_lock(&data.mutex2);
if(data.ptr_write==data.ptr_read) //生产者即将写的位置为消费者即将读的位置
pthread_cond_wait(&data.can_write,&data.mutex2);//等待消费者
data.d[data.ptr_write]=++value;//写入数据
printf("------add data:%d \n",data.d[data.ptr_write]);
data.ptr_write=(data.ptr_write+1)%length;//写指针后移
pthread_cond_signal(&data.can_read);//通知消费者
pthread_mutex_unlock(&data.mutex2);
}
}
四、线程消息模型
#include
#include
#include
struct Data
{
int d[5];
int ptr_write;//将要写入数据的位置
int ptr_read;//已经读取过数据的位置
int can_write;
int can_read;
};
pthread_t thread1,thread2;
struct Data data;
int value=0;
int length=5;
void *producer(void *pvoid)
{
int signum;
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig,SIGUSR1);
pthread_sigmask(SIG_BLOCK,&sig,NULL);//设置该线程的信号屏蔽字为SIGUSR1
data.d[data.ptr_write]=++value;
printf("produce new data : %d\n",data.d[data.ptr_write]);
data.ptr_write=(data.ptr_write+1)%length;
while(1)
{
sleep(rand()%3); //随机睡眠
if(data.ptr_write==data.ptr_read)
{
data.can_write=0;
sigwait(&sig,&signum);//睡眠等待SIGUSR1信号的到来
}
data.d[data.ptr_write]=++value;
printf("produce new data : %d\n",data.d[data.ptr_write]);
data.ptr_write=(data.ptr_write+1)%length;
if(data.can_read==0)
{
data.can_read=1;
pthread_kill(thread2,SIGUSR2);
}
}
}
void *consumer(void *pvoid)
{
sleep(1);
int signum;
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig,SIGUSR2);
pthread_sigmask(SIG_BLOCK,&sig,NULL);//设置该线程的信号屏蔽字为SIGUSR2
while(1)
{
sleep(rand()%3); //随机睡眠
if((data.ptr_read+1)%length==data.ptr_write)
{
data.can_read=0;
sigwait(&sig,&signum);//睡眠等待SIGUSR1信号的到来
}
printf("------consumer new data : %d\n",data.d[(data.ptr_read+1)%length]);
data.ptr_read=(data.ptr_read+1)%length;
if(data.can_write==0)
{
data.can_write=1;
pthread_kill(thread1,SIGUSR1);
}
}
}
void main()
{
data.ptr_read=4;
data.ptr_write=0;
data.can_read=0;
data.can_write=1;
struct sigaction act;
act.sa_handler=SIG_IGN;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigaction(SIGUSR1,&act,0);//设置信号SIGUSR1的处理方式忽略
sigaction(SIGUSR2,&act,0);//设置信号SIGUSR1的处理方式忽略
pthread_create(&thread1,NULL,producer,NULL);
pthread_create(&thread2,NULL,consumer,NULL);
pthread_detach(thread1);
pthread_detach(thread2);
while(1)
{
sleep(2);
}
}
编译命令gcc -D_REENTRANT -lpthread test4.c -o test4