以下列表显示了令牌机制的另一种实现。该实现仍使用 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。