线程项目问题 |如何从 C 中的多个线程 wait/signal
Thread project problem | How to wait/signal from multiple thread in C
我实际上在做一个个人项目,我必须制作一个模拟这种行为的小程序:
有两种人:村民和德鲁伊(Asterix/Obelix)
每个 VILLAGER 都由一个 id(村民唯一的编号)标识。它会在离开前战斗“nb_fights”时间
战场。每次战斗前,它必须从罐子里取出一份魔法药水(如果罐子是空的,
它必须通知德鲁伊并等到罐子重新装满)。
DRUID 将等待村民的召唤;然后它将用 pot_size 份重新装满锅。当nb_refills
已完成,德鲁伊 运行 配料用完,它的线程必须停止。
一切都在参数中给出 (./panoramix )
好吧,实际上我完成了项目的 70%,但我无法调用德鲁伊来补充药水。
有村民线程的代码:
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 = 战斗次数(我们减少)
然后是德鲁伊线程:
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");
}
我想知道当“pot_size”为空(=0)时如何“召唤”德鲁伊。
我试着在 if 条件下用信号量来做:
if (pot_size == 0) {
printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
sem_wait(&test);
}
(等druid填满pot_size然后继续程序)不过我觉得这样不太好
编辑:完整代码:
#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);
}
- 将游戏所需的所有全局变量分组到一个
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;
druid()
不应调用 sem_wait()
,因为这是生产者。如果所有村民都打完了,检查他们是否都在睡觉,return/exit 如果他们在睡觉。
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;
}
- 村民需要检查德鲁伊是否已经入睡,即。用尽所有 refill-quota。因为,只有一个线程有互斥体并且没有药水可以消耗,我们可以阻塞
sem_wait()
以便德鲁伊重新填充 & 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;
}
- 修改后的
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);
}
使用strtol()
代替atoi()
,它给了你更多的控制权:How to convert a string to integer in C?
如果线程没有向使用 pthread_join()
等待它们的线程返回任何有意义的内容,您可以使用 pthread_detach()
。
我实际上在做一个个人项目,我必须制作一个模拟这种行为的小程序:
有两种人:村民和德鲁伊(Asterix/Obelix)
每个 VILLAGER 都由一个 id(村民唯一的编号)标识。它会在离开前战斗“nb_fights”时间 战场。每次战斗前,它必须从罐子里取出一份魔法药水(如果罐子是空的, 它必须通知德鲁伊并等到罐子重新装满)。
DRUID 将等待村民的召唤;然后它将用 pot_size 份重新装满锅。当nb_refills 已完成,德鲁伊 运行 配料用完,它的线程必须停止。
一切都在参数中给出 (./panoramix
好吧,实际上我完成了项目的 70%,但我无法调用德鲁伊来补充药水。
有村民线程的代码:
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 = 战斗次数(我们减少)
然后是德鲁伊线程:
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");
}
我想知道当“pot_size”为空(=0)时如何“召唤”德鲁伊。
我试着在 if 条件下用信号量来做:
if (pot_size == 0) {
printf("Villager %d: Hey Pano wake up! We need more potion.\n", id);
sem_wait(&test);
}
(等druid填满pot_size然后继续程序)不过我觉得这样不太好
编辑:完整代码:
#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);
}
- 将游戏所需的所有全局变量分组到一个
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;
druid()
不应调用sem_wait()
,因为这是生产者。如果所有村民都打完了,检查他们是否都在睡觉,return/exit 如果他们在睡觉。
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;
}
- 村民需要检查德鲁伊是否已经入睡,即。用尽所有 refill-quota。因为,只有一个线程有互斥体并且没有药水可以消耗,我们可以阻塞
sem_wait()
以便德鲁伊重新填充 &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;
}
- 修改后的
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);
}
使用
strtol()
代替atoi()
,它给了你更多的控制权:How to convert a string to integer in C?如果线程没有向使用
pthread_join()
等待它们的线程返回任何有意义的内容,您可以使用pthread_detach()
。