线程项目问题|如何从C中的多个线程等待/信号

发布于 2025-01-24 03:37:00 字数 4998 浏览 1 评论 0 原文

我实际上是在做一个个人项目,我必须制作一个模拟这种行为的小程序:

有两种类型的人:村民和德鲁伊(作为Asterix/obelix)

每个村民都由ID确定(这是一个独特的数字村民)。它将在离开之前与“ nb_fights”时间打架 战场。每次战斗之前 它必须告知德鲁伊,然后等到锅重新填充锅。

德鲁伊将等待一个村民召唤。然后,它将用锅尺寸的食物重新填充锅。当NB_Refills 已经完成了,德鲁伊的成分已经用光了,它的线程必须停止。

所有内容都以参数(./ panoramix< nb_villagers>< lt; pot_size> nb_fights> nb_refills> nb_refills> gt; gt; gt; gt; gt; gt;

实际上,我实际上是我做了70%的项目,但是我做了70%的人,但我却遇到了麻烦的是druiid druil druil the potions potions。

有村民线程的代码:

void *villager(void *args)
{
    int pNb = nbF;
    int id = *(int *)args;
    
    printf("Villager %d: Going into battle!\n", id);
    while (pNb > 0) {
        pthread_mutex_lock(&mpot);
        printf("Villager %d: I need a drink... I see %d servings left.\n", id, pot_size);
        if (pot_size == 0) {
            printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
            sem_wait(&test);
        }
        pot_size--;
        pthread_mutex_unlock(&mpot);
        pNb--;
        printf("Villager %d: Take that roman scum! Only %d left.\n", id, pNb);
    }
    printf("Villager %d: I’m going to sleep now.\n", id);
}

使用 pnb =战斗数(我们减少)

然后有德鲁伊线程:

void *druid(void *args)
{
    int sizepot = pot_size;

    printf("Druid: I'm ready... but sleepy...\n");
    while (nb_refills > 0) {
        while (pot_size != 0) {
        }
        sem_wait(&test);
        nb_refills--;
        printf("Druid: Ah! Yes, yes, I'm awake! Working on it! Beware I can only make %d more refills after this one.\n", nb_refills);
        pot_size = sizepot;
        sem_post(&test);
    }
    printf("Druid: I'm out of viscum. I'm going back to... zZz\n");
}

我想知道我如何“调用” druid当“ pot_size”为空(= 0)。

我试图在if条件下使用信号量进行操作:(

        if (pot_size == 0) {
            printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
            sem_wait(&test);
        }

等待填充pot_size然后继续程序的druid),但我认为这不是很好。

编辑:整个代码:

#include "struct.h"

sem_t test;
pthread_mutex_t mpot;
pthread_mutex_t fill;
int nbV = 0;
int pot_size = 0;
int nbF = 0;
int nb_refills = 0;

int errorhandler(int ac, char **av)
{
    if (ac != 5)
    {
        printf("USAGE: ./panoramix <nb_villagers> <pot_size> <nb_fights> <nb_refills> \nValues must be > 0.\n");
        return (84);
    }
    if (atoi(av[1]) == 0 || atoi(av[2]) == 0 || atoi(av[3]) == 0 || atoi(av[4]) == 0)
    {
        printf("USAGE: ./panoramix <nb_villagers> <pot_size> <nb_fights> <nb_refills> \nValues must be > 0.\n");
        return (84);
    }
    return (0);
}

void *druid(void *args)
{
    int sizepot = pot_size;

    printf("Druid: I'm ready... but sleepy...\n");
    while (nb_refills > 0) {
        while (pot_size != 0) {
            usleep(1000);
        }
        pthread_mutex_lock(&fill);
        nb_refills--;
        printf("Druid: Ah! Yes, yes, I'm awake! Working on it! Beware I can only make %d more refills after this one.\n", nb_refills);
        pot_size = sizepot;
        pthread_mutex_unlock(&fill);
    }
    printf("Druid: I'm out of viscum. I'm going back to... zZz\n");
}

void *villager(void *args)
{
    int pNb = nbF;
    int id = *(int *)args;
    
    printf("Villager %d: Going into battle!\n", id);
    while (pNb > 0) {
        pthread_mutex_lock(&mpot);
        printf("Villager %d: I need a drink... I see %d servings left.\n", id, pot_size);
        if (pot_size == 0) {
            printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
        }
        pot_size--;
        pthread_mutex_unlock(&mpot);
        pNb--;
        printf("Villager %d: Take that roman scum! Only %d left.\n", id, pNb);
    }
    printf("Villager %d: I’m going to sleep now.\n", id);
}

int main(int ac, char **av)
{
    if (errorhandler(ac, av) == 84)
        return (84);
    nbV = atoi(av[1]);
    pot_size = atoi(av[2]);
    nbF = atoi(av[3]);
    nb_refills = atoi(av[4]);
    pthread_t th[nbV];
    int i;

    pthread_mutex_init(&mpot, NULL);
    pthread_mutex_init(&fill, NULL);
    sem_init(&test, 0, 1);
    for (int i = 0; i < nbV; i++) {
        int *a = malloc(sizeof(int));
        *a = i;
        if (pthread_create(&th[i], NULL, &villager, a) != 0) {
            perror("Failed to create the Villager\n");
        }
    }
    pthread_create(&th[nbV], NULL, &druid, NULL);
    for (int i = 0; i < atoi(av[1]); i++) {
        if (pthread_join(th[i], NULL) != 0) {
            perror("Failed to launch the Villager\n");
        }
    }
    sem_destroy(&test);
    pthread_mutex_destroy(&mpot);
    pthread_mutex_destroy(&fill);
    return (0);
}

I'm actually doing a personal project where I have to make a little program that simulate this behavior :

There is 2 type of people : Villager and Druid (as Asterix/Obelix)

Each VILLAGER is identified by an id (a number unique to the villager). It will fight "nb_fights" time before leaving
the battlefield. Before each fight, it must take a serving of magical potion from the pot (if the pot is empty,
it must inform the druid and wait until the pot is refilled).

The DRUID will wait to be called by a villager; then it will refill the pot with pot_size servings. When nb_refills
have been done, the druid has run out of ingredients and it’s thread must stop.

Everything is given in the parameters (./panoramix <nb_villagers> <pot_size> <nb_fights> <nb_refills>)

Well, actually I did 70% of the project but I have trouble calling the druid to refill the potions.

There is the code for the Villager thread :

void *villager(void *args)
{
    int pNb = nbF;
    int id = *(int *)args;
    
    printf("Villager %d: Going into battle!\n", id);
    while (pNb > 0) {
        pthread_mutex_lock(&mpot);
        printf("Villager %d: I need a drink... I see %d servings left.\n", id, pot_size);
        if (pot_size == 0) {
            printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
            sem_wait(&test);
        }
        pot_size--;
        pthread_mutex_unlock(&mpot);
        pNb--;
        printf("Villager %d: Take that roman scum! Only %d left.\n", id, pNb);
    }
    printf("Villager %d: I’m going to sleep now.\n", id);
}

WITH pNb = Number of fight (that we decrement)

then there is the Druid thread :

void *druid(void *args)
{
    int sizepot = pot_size;

    printf("Druid: I'm ready... but sleepy...\n");
    while (nb_refills > 0) {
        while (pot_size != 0) {
        }
        sem_wait(&test);
        nb_refills--;
        printf("Druid: Ah! Yes, yes, I'm awake! Working on it! Beware I can only make %d more refills after this one.\n", nb_refills);
        pot_size = sizepot;
        sem_post(&test);
    }
    printf("Druid: I'm out of viscum. I'm going back to... zZz\n");
}

I would like to know how I can "call" the druid when the "pot_size" is empty (=0).

I tried to do it with semaphore in the if condition :

        if (pot_size == 0) {
            printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
            sem_wait(&test);
        }

(wait the druid for filling the pot_size and then continue the program) but I think this is not really good.

EDIT : THE ENTIRE CODE :

#include "struct.h"

sem_t test;
pthread_mutex_t mpot;
pthread_mutex_t fill;
int nbV = 0;
int pot_size = 0;
int nbF = 0;
int nb_refills = 0;

int errorhandler(int ac, char **av)
{
    if (ac != 5)
    {
        printf("USAGE: ./panoramix <nb_villagers> <pot_size> <nb_fights> <nb_refills> \nValues must be > 0.\n");
        return (84);
    }
    if (atoi(av[1]) == 0 || atoi(av[2]) == 0 || atoi(av[3]) == 0 || atoi(av[4]) == 0)
    {
        printf("USAGE: ./panoramix <nb_villagers> <pot_size> <nb_fights> <nb_refills> \nValues must be > 0.\n");
        return (84);
    }
    return (0);
}

void *druid(void *args)
{
    int sizepot = pot_size;

    printf("Druid: I'm ready... but sleepy...\n");
    while (nb_refills > 0) {
        while (pot_size != 0) {
            usleep(1000);
        }
        pthread_mutex_lock(&fill);
        nb_refills--;
        printf("Druid: Ah! Yes, yes, I'm awake! Working on it! Beware I can only make %d more refills after this one.\n", nb_refills);
        pot_size = sizepot;
        pthread_mutex_unlock(&fill);
    }
    printf("Druid: I'm out of viscum. I'm going back to... zZz\n");
}

void *villager(void *args)
{
    int pNb = nbF;
    int id = *(int *)args;
    
    printf("Villager %d: Going into battle!\n", id);
    while (pNb > 0) {
        pthread_mutex_lock(&mpot);
        printf("Villager %d: I need a drink... I see %d servings left.\n", id, pot_size);
        if (pot_size == 0) {
            printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
        }
        pot_size--;
        pthread_mutex_unlock(&mpot);
        pNb--;
        printf("Villager %d: Take that roman scum! Only %d left.\n", id, pNb);
    }
    printf("Villager %d: I’m going to sleep now.\n", id);
}

int main(int ac, char **av)
{
    if (errorhandler(ac, av) == 84)
        return (84);
    nbV = atoi(av[1]);
    pot_size = atoi(av[2]);
    nbF = atoi(av[3]);
    nb_refills = atoi(av[4]);
    pthread_t th[nbV];
    int i;

    pthread_mutex_init(&mpot, NULL);
    pthread_mutex_init(&fill, NULL);
    sem_init(&test, 0, 1);
    for (int i = 0; i < nbV; i++) {
        int *a = malloc(sizeof(int));
        *a = i;
        if (pthread_create(&th[i], NULL, &villager, a) != 0) {
            perror("Failed to create the Villager\n");
        }
    }
    pthread_create(&th[nbV], NULL, &druid, NULL);
    for (int i = 0; i < atoi(av[1]); i++) {
        if (pthread_join(th[i], NULL) != 0) {
            perror("Failed to launch the Villager\n");
        }
    }
    sem_destroy(&test);
    pthread_mutex_destroy(&mpot);
    pthread_mutex_destroy(&fill);
    return (0);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夏有森光若流苏 2025-01-31 03:37:00
  1. 将游戏所需的所有全局变量分组到 struct 中,在一个位置访问它们变得更容易。

typedef struct {
    int nbV;            // # of villagers
    int nbF;            // # of fights each villager takes on
    int nb_refills;     // # of potion refills that druid can make
    int pot_size;       // size of potion pot
    int potion_left;    // remaining potion in pot for consumption
    int druid_awake;    // flag only modified by druid
    int villagers_awake;
    sem_t pot_sem;
    pthread_mutex_t potion_mutex;
} sPanoramix_t;

sPanoramix_t pgame;
  1. druid()不应调用 sem_wait(),因为这是生产者。如果所有的村民都在战斗中完成,请检查所有村庄是否在睡觉,请返回/退出。
void *druid (void *args) {
    printf ("Druid: I'm ready... but sleepy...\n");
    pgame.druid_awake = 1;
    while (pgame.nb_refills > 0) {
        while (pgame.potion_left > 0) {
            if (!pgame.villagers_awake) {
                printf ("Druid: KILLED all of them today! Drinks are on me :-)\n");
                return NULL;
            }
            usleep (1000);
        }
        pgame.nb_refills--;
        printf ("Druid: Ah! Yes, yes, I'm awake! Working on it! Beware I can only make %d more refills after this one.\n", pgame.nb_refills);
        pgame.potion_left = pgame.pot_size;
        printf ("Druid: Kill those romans, potion refilled to %d\n", pgame.potion_left);
        sem_post (&pgame.pot_sem);
    }
    printf ("Druid: ALERT!! I'm out of viscum. I'm going back to... zZz\n");
    pgame.druid_awake = 0;    // druid is not available anymore
    return NULL;
}
  1. 村民需要检查德鲁伊是否入睡,即。用尽了所有补充点。从那以后,只有一个线程具有静音&amp;没有药水可以消耗,我们可以在 sem_wait()上阻止Druid重新填充&am​​p; sem_post()
void *villager (void *args) {
    int pNb = pgame.nbF;
    int id = (unsigned long)args;

    printf ("Villager %d: Going into battle!\n", id);
    while (pNb > 0) {
        pthread_mutex_lock (&pgame.potion_mutex);
        printf ("Villager %d: I need a drink... I see %d servings left.\n", id, pgame.potion_left);
        if (pgame.potion_left <= 0) {
            if (!pgame.druid_awake) {
                printf ("Villager %d: We're doomed now Pano!\n", id);
                pthread_mutex_unlock (&pgame.potion_mutex);
                return NULL;
            }
            printf ("Villager %d: Hey Pano wake up! We need more potion.\n", id);
            sem_wait (&pgame.pot_sem);
            printf ("Villager %d: Thank you PANO!\n", id);
        }
        pgame.potion_left--;
        pthread_mutex_unlock (&pgame.potion_mutex);
        pNb--;
        printf ("Villager %d: Take that Roman scum! Only %d left.\n", id, pNb);
        usleep (1000); // let other villagers take turn
    }
    printf ("Villager %d: KILLED my quota of Romans, I’m going to sleep now.\n", id);
    pthread_mutex_lock (&pgame.potion_mutex);
    pgame.villagers_awake --;
    pthread_mutex_unlock (&pgame.potion_mutex);

    return NULL;
}
  1. 修改后的 main()
int main (int ac, char **av) {
    if (errorhandler (ac, av) == 84)
        return (84);

    pgame.nbV = atoi (av[1]);
    pgame.villagers_awake = pgame.nbV;
    pgame.pot_size = atoi (av[2]);
    pgame.potion_left = pgame.pot_size;
    pgame.nbF = atoi (av[3]);
    pgame.nb_refills = atoi (av[4]);
    pthread_t th[pgame.nbV + 1];

    pthread_mutex_init (&pgame.potion_mutex, NULL);
    sem_init (&pgame.pot_sem, 0, 1);
    pthread_create (&th[0], NULL, druid, NULL);
    usleep (1000);
    for (int i = 1; i <= pgame.nbV; i++) {
        if (pthread_create (&th[i], NULL, villager, (void*) (unsigned long)i) != 0)
            perror ("Failed to create the Villager\n");
    }
    for (int i = 0; i <= pgame.nbV; i++) {
        if (pthread_join (th[i], NULL) != 0)
            perror ("Failed pthread_join()\n");
    }
    sem_destroy (&pgame.pot_sem);
    pthread_mutex_destroy (&pgame.potion_mutex);

    printf ("End of game Panoramix!\n");
    return (0);
}

  1. 使用 strtol()而不是 atoi(),它为您提供了更多的控制:如何将字符串转换为c?

    中的整数

  2. 中的整数,如果线程没有将任何有意义的线程返回使用 pthread_join( )

  1. Group all global variables you need for the game into a struct, becomes easier to access them at a location.

typedef struct {
    int nbV;            // # of villagers
    int nbF;            // # of fights each villager takes on
    int nb_refills;     // # of potion refills that druid can make
    int pot_size;       // size of potion pot
    int potion_left;    // remaining potion in pot for consumption
    int druid_awake;    // flag only modified by druid
    int villagers_awake;
    sem_t pot_sem;
    pthread_mutex_t potion_mutex;
} sPanoramix_t;

sPanoramix_t pgame;
  1. druid() should not call sem_wait(), as this is the producer. If all the villagers are done with fighting, check if all of them sleeping, return/exit if they're.
void *druid (void *args) {
    printf ("Druid: I'm ready... but sleepy...\n");
    pgame.druid_awake = 1;
    while (pgame.nb_refills > 0) {
        while (pgame.potion_left > 0) {
            if (!pgame.villagers_awake) {
                printf ("Druid: KILLED all of them today! Drinks are on me :-)\n");
                return NULL;
            }
            usleep (1000);
        }
        pgame.nb_refills--;
        printf ("Druid: Ah! Yes, yes, I'm awake! Working on it! Beware I can only make %d more refills after this one.\n", pgame.nb_refills);
        pgame.potion_left = pgame.pot_size;
        printf ("Druid: Kill those romans, potion refilled to %d\n", pgame.potion_left);
        sem_post (&pgame.pot_sem);
    }
    printf ("Druid: ALERT!! I'm out of viscum. I'm going back to... zZz\n");
    pgame.druid_awake = 0;    // druid is not available anymore
    return NULL;
}
  1. Villagers need to check if druid has gone to sleep, ie. exhausted all refill-quota. Since, only one thread has the mutex & no potion to consume, we can block on sem_wait() for druid to refill & sem_post().
void *villager (void *args) {
    int pNb = pgame.nbF;
    int id = (unsigned long)args;

    printf ("Villager %d: Going into battle!\n", id);
    while (pNb > 0) {
        pthread_mutex_lock (&pgame.potion_mutex);
        printf ("Villager %d: I need a drink... I see %d servings left.\n", id, pgame.potion_left);
        if (pgame.potion_left <= 0) {
            if (!pgame.druid_awake) {
                printf ("Villager %d: We're doomed now Pano!\n", id);
                pthread_mutex_unlock (&pgame.potion_mutex);
                return NULL;
            }
            printf ("Villager %d: Hey Pano wake up! We need more potion.\n", id);
            sem_wait (&pgame.pot_sem);
            printf ("Villager %d: Thank you PANO!\n", id);
        }
        pgame.potion_left--;
        pthread_mutex_unlock (&pgame.potion_mutex);
        pNb--;
        printf ("Villager %d: Take that Roman scum! Only %d left.\n", id, pNb);
        usleep (1000); // let other villagers take turn
    }
    printf ("Villager %d: KILLED my quota of Romans, I’m going to sleep now.\n", id);
    pthread_mutex_lock (&pgame.potion_mutex);
    pgame.villagers_awake --;
    pthread_mutex_unlock (&pgame.potion_mutex);

    return NULL;
}
  1. Modified main() :
int main (int ac, char **av) {
    if (errorhandler (ac, av) == 84)
        return (84);

    pgame.nbV = atoi (av[1]);
    pgame.villagers_awake = pgame.nbV;
    pgame.pot_size = atoi (av[2]);
    pgame.potion_left = pgame.pot_size;
    pgame.nbF = atoi (av[3]);
    pgame.nb_refills = atoi (av[4]);
    pthread_t th[pgame.nbV + 1];

    pthread_mutex_init (&pgame.potion_mutex, NULL);
    sem_init (&pgame.pot_sem, 0, 1);
    pthread_create (&th[0], NULL, druid, NULL);
    usleep (1000);
    for (int i = 1; i <= pgame.nbV; i++) {
        if (pthread_create (&th[i], NULL, villager, (void*) (unsigned long)i) != 0)
            perror ("Failed to create the Villager\n");
    }
    for (int i = 0; i <= pgame.nbV; i++) {
        if (pthread_join (th[i], NULL) != 0)
            perror ("Failed pthread_join()\n");
    }
    sem_destroy (&pgame.pot_sem);
    pthread_mutex_destroy (&pgame.potion_mutex);

    printf ("End of game Panoramix!\n");
    return (0);
}

  1. Use strtol() instead of atoi(), it gives you more control: How to convert a string to integer in C?

  2. You can use pthread_detach() if threads are not returning anything meaningful to a thread which waits for them with pthread_join().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文