Linux多线程是Linux型程序设计中十分重要概念,其中线程同步就更加重要了,生产者与消费者模型就是线程同步的经典例子,下面我将给你三种多线程实现生产者和消费者的例子。
一、生产者与消费者逻辑
关键的问题,在于生产者和消费者什么时候睡眠,又什么时候被唤醒,这就是生产者和消费者模型的关键。
缓冲区模型:
使用唤醒缓冲区,方向为逆时针。
read表示已经读取的位置
write表示即将写入的位置
缓冲区初始状态:
read不等于write。
生产者写满:
write==read,即将写的地方为刚读取的位置,就是下一个位置还没有读,所以代表写满。
消费者读完:
read+1=write,即将读的地方为即将写入得位置,还没有写,就不能再读了,这里就代表读完。
二、信号量模型
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <semaphore.h> //临界资源描述信息 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 <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <semaphore.h> //临界资源描述信息 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 <stdio.h> #include <pthread.h> #include <signal.h> 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