欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

哲学家问题

程序员文章站 2022-07-04 23:34:44
...

哲学家问题

有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。
哲学家进餐问题是一个多线程运用的经典例子,涉及到线程同步/互斥,临界区访问问题以及死锁问题。

可能导致的死锁问题:

如果5个哲学家都去拿他左边的筷子, 再拿右边的筷子, 会导致每个人都在等待筷子, 导致死锁问题.

解决办法1:

最多允许4个哲学家吃饭, 第5个人想要拿筷子, 要阻塞直到有1个哲学家吃完, 释放筷子.
那怎么让第5个线程想要拿筷子前, 阻塞住?

用信号量机制, 信号量初始化为4, 每个线程拿筷子前调用sem_wait(), 吃完后, 调用sem_post(), 这样在第5个线程要拿筷子时, 就会阻塞, 直到有一个线程吃完后才会解除阻塞, 从而避免了死锁.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

#define NUM 4
/**
 * 最多允许4个哲学家吃饭, 第5个想吃要阻塞直到有1个哲学家吃完
 */
pthread_mutex_t chops[5];
sem_t max_philosopher_num;

void think(int i) {
    printf("I am thinking %d\n", i);
    sleep(rand() % 3);
}

void hungry(int i) {
    printf("I am hungry %d\n", i);
    sleep(rand() % 3);
}

void eat(int i) {
    printf("I am eating %d\n", i);
    sleep(rand() % 3);
}

void *philosopher(void *arg) {
    int i = (int) arg;
    think(i);
    hungry(i);

    sem_wait(&max_philosopher_num);
    pthread_mutex_lock(&chops[i]);
    pthread_mutex_lock(&chops[(i + 1) % 5]);

    eat(i);

    pthread_mutex_unlock(&chops[i]);
    pthread_mutex_unlock(&chops[(i + 1) % 5]);
    sem_post(&max_philosopher_num);

    return NULL;
}

int main() {
    pthread_t tid[5];
    srand(time(NULL));

    for (int i = 0; i < 5; ++i) {
        pthread_mutex_init(&chops[i], NULL);
    }
    sem_init(&max_philosopher_num, 0, NUM);

    for (int i = 0; i < 5; ++i) {
        pthread_create(&tid[i], NULL, philosopher, (void *) i);
    }

    for (int i = 0; i < 5; ++i) {
        pthread_join(tid[i], NULL);
    }
    for (int i = 0; i < 5; ++i) {
        pthread_mutex_destroy(&chops[i]);
    }

    sem_destroy(&max_philosopher_num);
    return 0;
}

解决办法2:

如果想给某个哲学家筷子,就将他需要的所有筷子都给他,然后让他进餐,否则就一个都不给他。从而避免了死锁。
怎么实现呢?
搞一个全局互斥量, 然后在某个线程拿筷子前, 先去拿到这个全局互斥量, 然后在拿他所需要的2个筷子, 因为别的线程没有抢到这个互斥量, 所以不能拿筷子, 而在这个互斥量这里阻塞.
所以这个拿到互斥量的线程可以拿到所有的筷子进餐, 在进餐结束后释放这个互斥量即可.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

/**
 * 如果想给某个哲学家筷子,就将他需要的所有资源都给他,然后让他进餐,否则就一个都不给他。
 */

pthread_mutex_t mutex, chops[5];

void think(int i) {
    printf("I am thinking %d\n", i);
    sleep(rand() % 3);
}

void hungry(int i) {
    printf("I am hungry %d\n", i);
    sleep(rand() % 3);
}

void eat(int i) {
    printf("I am eating %d\n", i);
    sleep(rand() % 3);
}

void *philosopher(void *arg) {
    int i = (int)arg;

    think(i);
    hungry(i);

    pthread_mutex_lock(&mutex); // 阻止其他线程拿筷子
    pthread_mutex_lock(&chops[i]);
    pthread_mutex_lock(&chops[(i + 1) % 5]);
    pthread_mutex_unlock(&mutex);

    eat(i);

    pthread_mutex_unlock(&chops[i]);
    pthread_mutex_unlock(&chops[(i + 1) % 5]);
    return NULL;
}

int main() {
    pthread_t tid[5];
    srand(time(NULL));

    pthread_mutex_init(&mutex, NULL);
    for (int i = 0; i < 5; ++i) {
        pthread_mutex_init(&chops[i], NULL);
    }

    for (int i = 0; i < 5; ++i) {
        pthread_create(&tid[i], NULL, philosopher, (void *)i);
    }

    for (int i = 0; i < 5; ++i) {
        pthread_join(tid[i], NULL);
    }

    pthread_mutex_destroy(&mutex);
    for (int i = 0; i < 5; ++i) {
        pthread_mutex_destroy(&chops[i]);
    }

    return 0;
}

解决办法3:

让奇数号哲学家先去拿他左边的筷子, 然后再拿他右边的筷子, 偶数号哲学家相反, 这样可以保证至少有一个哲学家可以拿到2个筷子进餐然后再释放2个筷子.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t chops[5];

void think(int i) {
    printf("I am thinking %d\n", i);
    sleep(rand() % 3);
}

void hungry(int i) {
    printf("I am hungry %d\n", i);
    sleep(rand() % 3);
}

void eat(int i) {
    printf("I am eating %d\n", i);
    sleep(rand() % 3);
}

void *philosopher(void *arg) {
    int i = (int)arg;

    think(i);
    hungry(i);

    if (i % 2 == 0) {
        pthread_mutex_lock(&chops[(i + 1) % 5]);
        pthread_mutex_lock(&chops[i]);
        eat(i);
        pthread_mutex_unlock(&chops[i]);
        pthread_mutex_unlock(&chops[(i + 1) % 5]);
    } else {
        pthread_mutex_lock(&chops[i]);
        pthread_mutex_lock(&chops[(i + 1) % 5]);
        eat(i);
        pthread_mutex_unlock(&chops[i]);
        pthread_mutex_unlock(&chops[(i + 1) % 5]);
    }

    return NULL;
}

int main() {
    pthread_t tid[5];
    srand(time(NULL));

    for (int i = 0; i < 5; ++i) {
        pthread_mutex_init(&chops[i], NULL);
    }

    for (int i = 0; i < 5; ++i) {
        pthread_create(&tid[i], NULL, philosopher, (void *)i);
    }

    for (int i = 0; i < 5; ++i) {
        pthread_join(tid[i], NULL);
    }

    for (int i = 0; i < 5; ++i) {
        pthread_mutex_destroy(&chops[i]);
    }

    return 0;
}

这篇确实有点水…下篇绝对不水 !