哲学家问题(pthread & OpenMP)及其两种解法

哲学家问题
2019.12.26
一:问题描述
假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东⻄的时 候,他们就停止思考,思考的时候也停止吃东⻄。餐桌中间有一大碗意大利面,每两个哲学家之间 有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东⻄。他们只 能使用自己左右手边的那两只餐叉。 每个哲学家分为3个状态{thinking,trying,eating},分别代表思考,尝试获得叉子,吃面三个状 态。 哲学家从来不交谈,因此可能产生死锁,即某个时刻每个哲学家都拿着一只餐叉,永远都在等另一 只餐叉,也就是都处于trying的状态。

二:设计概要
解决本问题应采用多线程实现。
哲学家的刀叉由于是共享变量,所以需要作为临界区用互斥变量、信号量等机制实现互斥。本次采 用信号量实现互斥。也就需要为每个叉子定义相应的信号量,对于每一个线程,在访问共享变量叉 子的时候应先进行sem_wait,然后访问到两个叉子后,哲学家就能eating,而后进行sem_post解锁 这两个叉子。

三:避免死锁的策略
method1: 限制同一时刻最多只能有thread_count - 1个人进入餐厅尝试获取叉子,剩下的一位只有 当有人吃好出来的时候再进去,因此能够保证至少有一个人能够获得两个叉子,从而eating,这样的 话永远不会导致所有人都在trying,即能够避免死锁。 实现方法为:增加一个信号量,设置其初值为thread_count - 1,在每个线程任务开始前先 sem_wait,任务结束后sem_post。
Method2: 分奇偶数编号进餐 奇数号哲学家先取左手的叉子,然后取右手的叉子,而偶数哲学家先取右手的叉子,然后再取左手 的叉子。 实现方法为:判断当前线程的编号,如果是奇数号线程的话就先sem_wait左边的叉子,右边类似; 如果是偶数线程的话就先sem_wait右边的叉子,左边类似。

四:实验结果(全部成功)

  1. normal
    在这里插入图片描述
  2. method1
    在这里插入图片描述
  3. method2
    在这里插入图片描述

五:分析

六:原代码

1. pthread版本
/*	
	pthread
    无先生_ 2019.12.26
    命令以以下形式:./deadlock -normal 5
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>

long thread_count;   //线程个数,即哲学家个数

void *Thread_normal(void *rank);    //死锁状态
void *Thread_method1(void *rank);   //解锁方法1
void *Thread_method2(void *rank);   //解锁方法2

sem_t *sem_fork;    //定义一组叉子的信号量
sem_t sem_waiter;	//method1,用于确保至少一个人不能进餐

int main(int argc, char *argv[])
{
    long thread;
    pthread_t *thread_handles;
    thread_count = strtol(argv[2], NULL, 10);  //获取线程(哲学家)个数

    thread_handles = malloc(thread_count * sizeof(pthread_t));
    sem_fork = malloc (thread_count * sizeof(sem_t));

    sem_init(&sem_waiter, 0, 4);    //初始化waiter信号量
    long i;
    for(i = 0; i < thread_count; i ++ ){
        sem_init(&sem_fork[i], 0, 1);             //初始化所有叉子信号量
    }
    
    for(thread = 0; thread < thread_count; thread ++ ){ //创建线程
	if(strcmp(argv[1], "-normal") == 0 || strcmp(argv[1], "normal") == 0)   //进入相应的模式
        	pthread_create(&thread_handles[thread], NULL, Thread_normal, (void *)thread);
	else if(strcmp(argv[1], "-method1") == 0 || strcmp(argv[1], "method1") == 0)
        	pthread_create(&thread_handles[thread], NULL, Thread_method1, (void *)thread);
	else if(strcmp(argv[1], "-method2") == 0 || strcmp(argv[1], "method2") == 0)
        	pthread_create(&thread_handles[thread], NULL, Thread_method2, (void *)thread);
    }

    for(thread = 0; thread < thread_count; thread ++ )  //join所有线程
        pthread_join(thread_handles[thread], NULL);
    
    for(i = 0; i < thread_count; i ++ ){    //删除所有叉子信号量
        sem_destroy(&sem_fork[i]);
    }
    sem_destroy(&sem_waiter);   //删除waiter信号量
    
    free(thread_handles);
    free(sem_fork);
    return 0;
}

//陷入死锁
void *Thread_normal(void *rank)
{
    long my_rank = (long)rank;
    long my_left, my_right;     //表示每个哲学家左右的叉子
    my_left = my_rank;
    my_right = (my_rank + 1) % thread_count;
    while(1){
	    printf("Philosopher %ld is thinking\n", my_rank);
	    usleep(50); //think 50 ms
	    
	    printf("Philosopher %ld is trying\n", my_rank);
	    
	    sem_wait(&sem_fork[my_left]);
	    sem_wait(&sem_fork[my_right]);
	    
	    printf("Philosopher %ld is eating\n", my_rank);
	    usleep(100); //eating 100 ms

	    sem_post(&sem_fork[my_left]);
	    sem_post(&sem_fork[my_right]);
    }
    return NULL;
}

//解锁方法1
void *Thread_method1(void *rank)
{
    long my_rank = (long)rank;
    long my_left, my_right;    //表示每个哲学家左右的叉子
    my_left = my_rank;
    my_right = (my_rank + 1) % thread_count;
    sem_wait(&sem_waiter);  //添加一个waiter信号量确保每个时刻最少一个人没有进去就餐
    while(1){
	    printf("Philosopher %ld is thinking\n", my_rank);
	    usleep(50);     //think 50 ms
	    
	    printf("Philosopher %ld is trying\n", my_rank);
	    
	    sem_wait(&sem_fork[my_left]);
	    sem_wait(&sem_fork[my_right]);
	    
	    printf("Philosopher %ld is eating\n", my_rank);
	    usleep(100);    //eating 100 ms

	    sem_post(&sem_fork[my_left]);
	    sem_post(&sem_fork[my_right]);
    }
    sem_post(&sem_waiter);
    return NULL;
}

//解锁方法2
void *Thread_method2(void *rank)
{
    long my_rank = (long)rank;
    long my_left, my_right;    //表示每个哲学家左右的叉子
    if(my_rank % 2 == 1){
	my_left = (my_rank + 1) % thread_count;
	my_right = my_rank;
    }
    else{
	my_left = my_rank;
	my_right = (my_rank + 1) % thread_count;
    }
    while(1){
	    printf("Philosopher %ld is thinking\n", my_rank);
	    usleep(50);     //think 50 ms
	    
	    printf("Philosopher %ld is trying\n", my_rank);
	    
	    sem_wait(&sem_fork[my_left]);
	    sem_wait(&sem_fork[my_right]);
	    
	    printf("Philosopher %ld is eating\n", my_rank);
	    usleep(100);    //eating 100 ms

	    sem_post(&sem_fork[my_left]);
	    sem_post(&sem_fork[my_right]);
    }
    return NULL;
}
2.openmp版本
/*
    openmp
    无先生_ 2019.12.26
    命令以以下形式:./deadlock -normal 5
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <omp.h>

void *Thread_normal(void);    //死锁状态
void *Thread_method1(void);   //解锁方法1
void *Thread_method2(void);   //解锁方法2

sem_t *sem_fork;    //定义一组叉子的信号量
sem_t sem_waiter;    //method1,用于确保至少一个人不能进餐

int main(int argc, char *argv[])
{
    long thread;
    long thread_count = strtol(argv[2], NULL, 10);  //获取线程(哲学家)个数
    sem_fork = malloc (thread_count * sizeof(sem_t));

    sem_init(&sem_waiter, 0, 4);    //初始化waiter信号量
    long i;
    for(i = 0; i < thread_count; i ++ ){
        sem_init(&sem_fork[i], 0, 1);             //初始化所有叉子信号量
    }
    
    if(strcmp(argv[1], "-normal") == 0 || strcmp(argv[1], "normal") == 0){   //进入相应的模式
    #   pragma omp parallel num_threads(thread_count)
        Thread_normal();
    }
    else if(strcmp(argv[1], "-method1") == 0 || strcmp(argv[1], "method1") == 0){
    #   pragma omp parallel num_threads(thread_count)
        Thread_method1();
    }
    else if(strcmp(argv[1], "-method2") == 0 || strcmp(argv[1], "method2") == 0){
    #   pragma omp parallel num_threads(thread_count)
        Thread_method2();
    }
    for(i = 0; i < thread_count; i ++ ){    //删除所有叉子信号量
        sem_destroy(&sem_fork[i]);
    }
    sem_destroy(&sem_waiter);   //删除waiter信号量
    
    free(sem_fork);
    return 0;
}

//陷入死锁
void *Thread_normal(void)
{
    long my_rank = omp_get_thread_num();
    long thread_count = omp_get_num_threads();
    long my_left, my_right;     //表示每个哲学家左右的叉子
    my_left = my_rank;
    my_right = (my_rank + 1) % thread_count;
    while(1){
        printf("Philosopher %ld is thinking\n", my_rank);
        usleep(50); //think 50 ms
        
        printf("Philosopher %ld is trying\n", my_rank);
        
        sem_wait(&sem_fork[my_left]);
        sem_wait(&sem_fork[my_right]);
        
        printf("Philosopher %ld is eating\n", my_rank);
        usleep(100); //eating 100 ms

        sem_post(&sem_fork[my_left]);
        sem_post(&sem_fork[my_right]);
    }
}

//解锁方法1
void *Thread_method1(void)
{
    long my_rank = omp_get_thread_num();
    long thread_count = omp_get_num_threads();
    long my_left, my_right;    //表示每个哲学家左右的叉子
    my_left = my_rank;
    my_right = (my_rank + 1) % thread_count;
    sem_wait(&sem_waiter);  //添加一个waiter信号量确保每个时刻最少一个人没有进去就餐
    while(1){
        printf("Philosopher %ld is thinking\n", my_rank);
        usleep(50);     //think 50 ms
        
        printf("Philosopher %ld is trying\n", my_rank);
        
        sem_wait(&sem_fork[my_left]);
        sem_wait(&sem_fork[my_right]);
        
        printf("Philosopher %ld is eating\n", my_rank);
        usleep(100);    //eating 100 ms

        sem_post(&sem_fork[my_left]);
        sem_post(&sem_fork[my_right]);
    }
    sem_post(&sem_waiter);
}

//解锁方法2
void *Thread_method2(void)
{
    long my_rank = omp_get_thread_num();
    long thread_count = omp_get_num_threads();
    long my_first, my_second;   //表示每个哲学家两次拿叉子的顺序
    if(my_rank % 2 == 1){       //如果是奇数号哲学家,则先拿右边的叉子,再拿左边叉子
        my_first = (my_rank + 1) % thread_count;
        my_second = my_rank;
    }
    else{                       //如果是偶数号哲学家,则先拿左边的叉子,再拿右边叉子
        my_first = my_rank;
        my_second = (my_rank + 1) % thread_count;
    }
    while(1){
        printf("Philosopher %ld is thinking\n", my_rank);
        usleep(50);     //think 50 ms
        
        printf("Philosopher %ld is trying\n", my_rank);
        
        sem_wait(&sem_fork[my_first]);
        sem_wait(&sem_fork[my_second]);
        
        printf("Philosopher %ld is eating\n", my_rank);
        usleep(100);    //eating 100 ms

        sem_post(&sem_fork[my_first]);
        sem_post(&sem_fork[my_second]);
    }
}

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值