下面列出了令牌机制的另一种实现。该实现仍然使用四个令牌,所以最多有四个就餐者可同时尝试进餐。但是,该实现使用 sem_wait() 和 sem_post() 信号例程限制就餐的哲学家人数。该版本的源文件称为 din_philo_fix2.c。
下面列出了详细的 din_philo_fix2.c:
1 /*
2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
3 * @(#)din_philo_fix2.c 1.3 (Oracle) 10/03/26
4 */
5
6 #include <pthread.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <semaphore.h>
13
14 #define PHILOS 5
15 #define DELAY 5000
16 #define FOOD 100
17
18 void *philosopher (void *id);
19 void grab_chopstick (int,
20 int,
21 char *);
22 void down_chopsticks (int,
23 int);
24 int food_on_table ();
25 void get_token ();
26 void return_token ();
27
28 pthread_mutex_t chopstick[PHILOS];
29 pthread_t philo[PHILOS];
30 pthread_mutex_t food_lock;
31 int sleep_seconds = 0;
32 sem_t num_can_eat_sem;
33
34
35 int
36 main (int argn,
37 char **argv)
38 {
39 int i;
40
41 pthread_mutex_init (&food_lock, NULL);
42 sem_init(&num_can_eat_sem, 0, PHILOS - 1);
43 for (i = 0; i < PHILOS; i++)
44 pthread_mutex_init (&chopstick[i], NULL);
45 for (i = 0; i < PHILOS; i++)
46 pthread_create (&philo[i], NULL, philosopher, (void *)i);
47 for (i = 0; i < PHILOS; i++)
48 pthread_join (philo[i], NULL);
49 return 0;
50 }
51
52 void *
53 philosopher (void *num)
54 {
55 int id;
56 int i, left_chopstick, right_chopstick, f;
57
58 id = (int)num;
59 printf ("Philosopher %d is done thinking and now ready to eat.\n", id);
60 right_chopstick = id;
61 left_chopstick = id + 1;
62
63 /* Wrap around the chopsticks. */
64 if (left_chopstick == PHILOS)
65 left_chopstick = 0;
66
67 while (f = food_on_table ()) {
68 get_token ();
69
70 grab_chopstick (id, right_chopstick, "right ");
71 grab_chopstick (id, left_chopstick, "left");
72
73 printf ("Philosopher %d: eating.\n", id);
74 usleep (DELAY * (FOOD - f + 1));
75 down_chopsticks (left_chopstick, right_chopstick);
76
77 return_token ();
78 }
79
80 printf ("Philosopher %d is done eating.\n", id);
81 return (NULL);
82 }
83
84 int
85 food_on_table ()
86 {
87 static int food = FOOD;
88 int myfood;
89
90 pthread_mutex_lock (&food_lock);
91 if (food > 0) {
92 food--;
93 }
94 myfood = food;
95 pthread_mutex_unlock (&food_lock);
96 return myfood;
97 }
98
99 void
100 grab_chopstick (int phil,
101 int c,
102 char *hand)
103 {
104 pthread_mutex_lock (&chopstick[c]);
105 printf ("Philosopher %d: got %s chopstick %d\n", phil, hand, c);
106 }
107
108 void
109 down_chopsticks (int c1,
110 int c2)
111 {
112 pthread_mutex_unlock (&chopstick[c1]);
113 pthread_mutex_unlock (&chopstick[c2]);
114 }
115
116
117 void
118 get_token ()
119 {
120 sem_wait(&num_can_eat_sem);
121 }
122
123 void
124 return_token ()
125 {
126 sem_post(&num_can_eat_sem);
127 }
这一新实现使用信号 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() 的调用,并判定并非所有的哲学家都同时尝试进餐。
要编译 din_philo_fix2.c,请使用以下命令:
% cc -g -lrt -o din_philo_fix2 din_philo_fix2.c
如果多次运行程序 din_philo_fix2 的这一新实现,将会发现它每次都正常终止,不会挂起。
在这一新的二进制代码上创建实验:
% collect -r deadlock -o din_philo_fix2.1.er din_philo_fix2
您将发现线程分析器不报告 din_philo_fix2.1.er 实验中的任何实际死锁或潜在死锁,如下图所示。
图 3-7 未报告 din_philo_fix2.c 中的任何死锁
有关线程分析器可识别的线程和内存分配 API 的列表,请参见Appendix A, 线程分析器可识别的 API。