以下列表显示了令牌机制的另一种实现。该实现仍使用 4 个令牌,因此同时尝试进餐的人数不会超过 4 个。但是,该实现使用 sem_wait() 和 sem_post() 信号例程来限制进餐的哲人数量。该版本的源文件称为 din_philo_fix2.c。
必须使用 -lrt 编译 din_philo_fix2.c,以链接正确的信号例程。
以下列表详细说明了 din_philo_fix2.c:
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <errno.h> 6 #include <assert.h> 7 #include <semaphore.h> 8 9 #define PHILOS 5 10 #define DELAY 5000 11 #define FOOD 50 12 13 void *philosopher (void *id); 14 void grab_chopstick (int, 15 int, 16 char *); 17 void down_chopsticks (int, 18 int); 19 int food_on_table (); 20 int get_token (); 21 void return_token (); 22 23 pthread_mutex_t chopstick[PHILOS]; 24 pthread_t philo[PHILOS]; 25 pthread_mutex_t food_lock; 26 int sleep_seconds = 0; 27 sem_t num_can_eat_sem; 28 29 30 int 31 main (int argn, 32 char **argv) 33 { 34 int i; 35 36 pthread_mutex_init (&food_lock, NULL); 37 sem_init(&num_can_eat_sem, 0, PHILOS - 1); 38 for (i = 0; i < PHILOS; i++) 39 pthread_mutex_init (&chopstick[i], NULL); 40 for (i = 0; i < PHILOS; i++) 41 pthread_create (&philo[i], NULL, philosopher, (void *)i); 42 for (i = 0; i < PHILOS; i++) 43 pthread_join (philo[i], NULL); 44 return 0; 45 } 46 47 void * 48 philosopher (void *num) 49 { 50 int id; 51 int i, left_chopstick, right_chopstick, f; 52 53 id = (int)num; 54 printf ("Philosopher %d is done thinking and now ready to eat.\n", id); 55 right_chopstick = id; 56 left_chopstick = id + 1; 57 58 /* Wrap around the chopsticks. */ 59 if (left_chopstick == PHILOS) 60 left_chopstick = 0; 61 62 while (f = food_on_table ()) { 63 get_token (); 64 65 grab_chopstick (id, right_chopstick, "right "); 66 grab_chopstick (id, left_chopstick, "left"); 67 68 printf ("Philosopher %d: eating.\n", id); 69 usleep (DELAY * (FOOD - f + 1)); 70 down_chopsticks (left_chopstick, right_chopstick); 71 72 return_token (); 73 } 74 75 printf ("Philosopher %d is done eating.\n", id); 76 return (NULL); 77 } 78 79 int 80 food_on_table () 81 { 82 static int food = FOOD; 83 int myfood; 84 85 pthread_mutex_lock (&food_lock); 86 if (food > 0) { 87 food--; 88 } 89 myfood = food; 90 pthread_mutex_unlock (&food_lock); 91 return myfood; 92 } 93 94 void 95 grab_chopstick (int phil, 96 int c, 97 char *hand) 98 { 99 pthread_mutex_lock (&chopstick[c]); 100 printf ("Philosopher %d: got %s chopstick %d\n", phil, hand, c); 101 } 102 103 void 104 down_chopsticks (int c1, 105 int c2) 106 { 107 pthread_mutex_unlock (&chopstick[c1]); 108 pthread_mutex_unlock (&chopstick[c2]); 109 } 110 111 112 int 113 get_token () 114 { 115 sem_wait(&num_can_eat_sem); 116 } 117 118 void 119 return_token () 120 { 121 sem_post(&num_can_eat_sem); 122 }
这一新实现使用信号 num_can_eat_sem
来限制同时进餐的哲人数量。信号 num_can_eat_sem
初始化为 4,比哲人的数量少一。在尝试进餐前,哲人将调用 get_token(),从而会调用 sem_wait(&num_can_eat_sem)
。调用 sem_wait() 将导致发出调用的哲人进入等待状态,直到信号值为正,然后通过从值中减 1 来更改信号的值。当一位哲人进完餐后,他将调用 return_token(),从而会调用 sem_post(&num_can_eat_sem)
。调用 sem_post() 可将信号值加 1。线程分析器可识别对 sem_wait() 和 sem_post() 的调用,并确定并非所有哲人都会同时尝试进餐。
如果多次运行这一新的程序实现,您将发现该程序每次都会正常终止,不会挂起。此外,还将发现线程分析器不会报告任何实际死锁或潜在死锁,如下面的屏幕抓图所示:
有关线程分析器可识别的线程和内存分配 API 的列表,请参见附录 A,线程分析器用户 API。