生产者消费者问题,又有界缓冲区问题。两个进程共享一个一个公共的固定大小的缓冲区。其中一个是生产者,将信息放入缓冲区,另一个是消费者,从缓冲区中取信息。
问题的关键在于缓冲区已满,而此时生产者还想往其中放入一个新的数据的情况。其解决办法是让生产者睡眠,待消费者从缓冲区中取出一个或多个数据时再唤醒它,同样的, 当消费者试图从缓冲区中取数据而发现缓冲区空时,消费者就睡眠,直到消费者向其中放一些数据后再将其唤醒。
上述方法可以用互斥量解决,程序代码:
1 #include<sys/types.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 #include<stdio.h> 5 #include<pthread.h> 6 #include<semaphore.h> 7 8 //消费者进程 9 void *thread_consumer(void *ptr); 10 //生产者进程 11 void *thread_producer(void *ptr); 12 13 #define MAX 100000 /*生产者需要生产的数量*/ 14 pthread_mutex_t the_mutex; /*互斥量*/ 15 pthread_cond_t condc,condp;/*生产者和消费者的线程条件变量*/ 16 17 //初始时缓冲区中没有数据 18 int buffer=0; 19 20 21 int buffer_max=0;/*用于记录缓冲区最多被用了多少*/ 22 23 #define BUFFER_SIZE 10000 /*缓冲区大小*/ 24 25 /*消费者线程*/ 26 void *thread_consumer(void *arg) 27 { 28 int i; 29 //消费完指定数目 退出线程 30 for(i=0;i<MAX;i++) 31 { 32 //互斥量加锁,线程同步 33 pthread_mutex_lock(&the_mutex); 34 /*缓冲区空 该进程睡眠 睡眠时pthread_cond_wait函数会对互斥量the_mutex解锁,这样生产者线程可以正常工作*/ 35 while(buffer==0) 36 { 37 pthread_cond_wait(&condc,&the_mutex); 38 } 39 //缓冲区非空 此时pthread_cond_wait又会对互斥量再次加锁 40 --buffer; //消耗一个元素 41 printf("consume an element, buffer=%d\n",buffer); //打印缓冲区中剩余元素 42 //如果缓冲区中元素个数为0 唤醒生产者线程 43 if(buffer==0) 44 { 45 pthread_cond_signal(&condp); 46 } 47 //释放互斥量 48 pthread_mutex_unlock(&the_mutex); 49 } 50 pthread_exit(NULL); 51 } 52 53 /*生产者线程*/ 54 void *thread_producer(void *ptr) 55 { 56 int i; 57 //消费完指定数目 退出线程 58 for(i=0;i<MAX;i++) 59 { 60 //互斥量加锁,线程同步 61 pthread_mutex_lock(&the_mutex); 62 //缓冲区满, 该进程睡眠 睡眠时pthread_cond_wait函数会对互斥量the_mutex解锁,这样消费者线程可以正常工作*/ 63 while(buffer==BUFFER_SIZE) 64 { 65 pthread_cond_wait(&condp,&the_mutex); 66 } 67 // 缓冲区未满时 此时pthread_cond_wait又会对互斥量再次加锁 68 ++buffer;//生产者增加一个数据 69 /*记录缓冲区最大的数据个数*/ 70 if(buffer>buffer_max) 71 buffer_max=buffer; 72 printf("produce an element, buffer=%d\n",buffer); 73 //如果缓冲区中元素个数大于0 唤醒消费者线程 74 if(buffer>0) 75 { 76 pthread_cond_signal(&condc); 77 } 78 //释放互斥量 79 pthread_mutex_unlock(&the_mutex); 80 } 81 } 82 83 int main() 84 { 85 pthread_t pro,con; 86 //初始化互斥量 87 pthread_mutex_init(&the_mutex,0); 88 //初始化线程条件变量 89 pthread_cond_init(&condc,0); 90 pthread_cond_init(&condp,0); 91 //创建生产者、消费者两个线程 92 pthread_create(&con,NULL,thread_consumer,NULL); 93 pthread_create(&pro,NULL,thread_producer,NULL); 94 //等待两个线程结束 95 pthread_join(pro,0); 96 pthread_join(con,0); 97 //清除变量 98 pthread_cond_destroy(&condc); 99 pthread_cond_destroy(&condp); 100 pthread_mutex_destroy(&the_mutex); 101 printf("buffer_max=%d",buffer_max); 102 }
在linux下运行时,可以看到两个线程交替运行,为了看到缓冲区最大能被添加到多少,我把缓冲区大小设置的很大,这样每次运行程序,打印的缓冲区的最大数都是不一样的,这跟实际的线程调度有关。
科学家就餐问题:
问题描述:假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。哲学家就餐问题有时也用米饭和筷子而不是意大利面和餐叉来描述,因为很明显,吃米饭必须用两根筷子。用下面的图描述很现实
问题解决:每个哲学家对应一个线程,程序中定义一个互斥量,对于每个线程进行访问其他哲学家状态时(关键代码)用互斥量进行加锁,这样也就避免了死锁的产生,访问到该哲学家处于饥饿时,同时旁边两位科学家并未处于进餐状态时,他就拿起左右两边的叉子进行吃饭,吃饭一段时间后,就放下叉子进行思考,思考一段时间后处于饥饿状态,重新开始试图拿起叉子吃饭,代码如下:
#include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<stdio.h> #include<pthread.h> #include<semaphore.h> #include<time.h> #define N 5 //哲学家数量 #define LEFT(i) (i+N-1)%N //左手边哲学家编号 #define RIGHT(i) (i+1)%N //右手边哲家编号 #define HUNGRY 0 //饥饿 #define THINKING 1 //思考 #define EATING 2 //吃饭 #define U_SECOND 1000000 //1秒对应的微秒数 pthread_mutex_t mutex; //互斥量 int state[N]; //记录每个哲学家状态 //每个哲学家的思考时间,吃饭时间,思考开始时间,吃饭开始时间 clock_t thinking_time[N], eating_time[N], start_eating_time[N], start_thinking_time[N]; //线程函数 void *thread_function(void *arg); int main() { pthread_mutex_init(&mutex, NULL); pthread_t a,b,c,d,e; //为每一个哲学家开启一个线程,传递哲学家编号 pthread_create(&a,NULL,thread_function,"0"); pthread_create(&b,NULL,thread_function,"1"); pthread_create(&c,NULL,thread_function,"2"); pthread_create(&d,NULL,thread_function,"3"); pthread_create(&e,NULL,thread_function,"4"); //初始化随机数种子 srand((unsigned int)(time(NULL))); while(1) { ; } } void *thread_function(void *arg) { char *a = (char *)arg; int num = a[0] - '0'; //根据传递参数获取哲学家编号 int rand_time; while(1) { //关键代码加锁 pthread_mutex_lock(&mutex); //如果该哲学家处于饥饿 并且 左右两位哲学家都没有在吃饭 就拿起叉子吃饭 if(state[num] == HUNGRY && state[LEFT(num)] != EATING && state[RIGHT(num)] != EATING) { state[num] = EATING; start_eating_time[num] = clock(); //记录开始吃饭时间 eating_time[num] = (rand() % 5 + 5) * U_SECOND; //随机生成吃饭时间 //输出状态 printf("state: %d %d %d %d %d\n",state[0],state[1],state[2],state[3],state[4]); //printf("%d is eating\n",num); } else if(state[num] == EATING) { //吃饭时间已到 ,开始思考 if(clock() - start_eating_time[num] >= eating_time[num]) // { state[num] = THINKING; //printf("%d is thinking\n",num); printf("state: %d %d %d %d %d\n",state[0],state[1],state[2],state[3],state[4]); start_thinking_time[num] = clock(); //记录开始思考时间 thinking_time[num] = (rand() % 10 + 10) * U_SECOND; //随机生成思考时间 } } else if(state[num] == THINKING) { //思考一定时间后,哲学家饿了,需要吃饭 if(clock() - start_thinking_time[num] >= thinking_time[num]) { state[num] = HUNGRY; printf("state: %d %d %d %d %d\n",state[0],state[1],state[2],state[3],state[4]); // printf("%d is hungry\n",num); } } pthread_mutex_unlock(&mutex); } }