食事する哲学者のシナリオは、次のような構成を持つ古典的なシナリオです。0 から 4 の番号が振られた 5 人の哲学者が丸いテーブルに向かって座り、思索しています。時間の経過とともに、空腹になり、食事をとることにします。テーブルには麺の盛られた大皿がありますが、哲学者が使用できる箸は 1 人に 1 本しかありません。食事をするためには、箸を共用することになります。それぞれの哲学者の、テーブルに向かって左側にある箸には、その哲学者と同じ番号が振られています。
哲学者は、最初に自分の箸 (自分と同じ番号の 1 本の箸) を取ろうとします。自分に割り当てられた箸を取ると、次に、自分の隣の人に割り当てられた箸に手を伸ばします。両方の箸を手にすると、食事ができます。食事を終えると、哲学者はテーブル上の元の位置に 2 本の箸を戻します。麺がなくなるまで、このプロセスが繰り返されます。
実デッドロックは、哲学者全員がそれぞれに自分の箸を手にしていて、隣の哲学者の箸が使い終わるのを待つときに発生します。
哲学者 0 は箸 0 を手にしていて、箸 1 を待つ |
哲学者 1 は箸 1 を手にしていて、箸 2 を待つ |
哲学者 2 は箸 2 を手にしていて、箸 3 を待つ |
哲学者 3 は箸 3 を手にしていて、箸 4 を待つ |
哲学者 4 は箸 4 を手にしていて、箸 0 を待つ |
この状況では誰も食事をすることができず、哲学者はデッドロック状態となります。プログラムを何回か実行します。実行が滞ることもあれば、最後まで実行されることもあることがわかります。
食事する哲学者プログラムを実行して、実行が完了するか、またはデッドロックになるかを確認します。 次の実行例に見られるように、実行は滞る可能性があります。
prompt% cc din_phil.c -mt prompt% a.out Philosopher 0 is done thinking and now ready to eat. Philosopher 2 is done thinking and now ready to eat. Philosopher 2: got right chopstick 2 Philosopher 2: got left chopstick 3 Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 4 is done thinking and now ready to eat. Philosopher 4: got right chopstick 4 Philosopher 2: eating. Philosopher 3 is done thinking and now ready to eat. Philosopher 1 is done thinking and now ready to eat. Philosopher 0: got right chopstick 0 Philosopher 3: got right chopstick 3 Philosopher 2: got right chopstick 2 Philosopher 1: got right chopstick 1 (hang) CTRL-C を押して実行を終了します
潜在的デッドロックに対して考えられる 1 つの解決策は、哲学者 1 が時間を置いてから箸を取るようにすることです。コード面から言うと、箸を取ろうとする前に、指定した時間 (sleep_seconds)、 哲学者 1 をスリープさせることができます。このスリープが十分に長ければ、プログラムは実デッドロックなしに終了する可能性があります。実行可能ファイルの引数としてスリープする秒数を指定できます。引数を指定しなかった場合、哲学者はスリープしません。
次の擬似コードは、各哲学者のロジックを示しています。
while (テーブルにはまだ食事がある) { if (スリープのための引数が指定されていて、自身は哲学者 1 である) { 指定された時間スリープする } 右の箸を持つ 左の箸を持つ 食事をする 左の箸を置く 右の箸を置く }
次のリストは、哲学者 1 が 30 秒経過してから自分の箸を取るようなプログラムの実行を示しています。プログラムは最後まで実行され、5 人の哲学者全員が食事を終えます。
% a.out 30 Philosopher 0 is done thinking and now ready to eat. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 4 is done thinking and now ready to eat. Philosopher 4: got right chopstick 4 Philosopher 3 is done thinking and now ready to eat. Philosopher 3: got right chopstick 3 Philosopher 0: eating. Philosopher 2 is done thinking and now ready to eat. Philosopher 2: got right chopstick 2 Philosopher 1 is done thinking and now ready to eat. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. ... Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0: got right chopstick 0 Philosopher 0: got left chopstick 1 Philosopher 0: eating. Philosopher 0 is done eating. Philosopher 4: got left chopstick 0 Philosopher 4: eating. Philosopher 4 is done eating. Philosopher 3: got left chopstick 4 Philosopher 3: eating. Philosopher 3 is done eating. Philosopher 2: got left chopstick 3 Philosopher 2: eating. Philosopher 2 is done eating. Philosopher 1: got right chopstick 1 Philosopher 1: got left chopstick 2 Philosopher 1: eating. Philosopher 1 is done eating. % 実行は正常に終了します
スリープ引数の値を変更しながら、何回かプログラムを実行してみます。哲学者 1 がほんの短い時間で箸を取ろうとした場合はどうなりますか。また、もっと長い時間待つようにした場合はどうですか。実行可能ファイル a.out のスリープ引数の値を変更してみてください。スリープ引数あり、またはスリープ引数なしで、何回かプログラムを実行します。プログラムの実行が滞ることがありますが、最後まで実行されることもあります。プログラムの実行が滞るかどうかは、スレッドのスケジューリングと、スレッドによるロックの要求のタイミングによって異なります。