linux下多线程互斥量实现生产者--消费者问题和哲学家就餐问题

生产者消费者问题,又有界缓冲区问题。两个进程共享一个一个公共的固定大小的缓冲区。其中一个是生产者,将信息放入缓冲区,另一个是消费者,从缓冲区中取信息。

问题的关键在于缓冲区已满,而此时生产者还想往其中放入一个新的数据的情况。其解决办法是让生产者睡眠,待消费者从缓冲区中取出一个或多个数据时再唤醒它,同样的, 当消费者试图从缓冲区中取数据而发现缓冲区空时,消费者就睡眠,直到消费者向其中放一些数据后再将其唤醒。

上述方法可以用互斥量解决,程序代码:

  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);       
    } 
}

 

posted @ 2016-04-21 10:01  渣码农  阅读(5270)  评论(0编辑  收藏  举报