Sun Studio 12: スレッドアナライザユーザーズガイド

3.5.1 トークンによる哲学者の制限

次のリストは、トークンを用いたシステムを使用するように修正された食事する哲学者プログラムのバージョンを示しています。このバージョンでは、食事をする人数より 1 つ少ない 4 つのトークンが組み込まれているため、同時に食事をできる哲学者が 4 人を超えることはありません。 このバージョンのプログラム名は din_philo_fix1.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	
     8	#define PHILOS 5
     9	#define DELAY 5000
    10	#define FOOD 50
    11	
    12	void *philosopher (void *id);
    13	void grab_chopstick (int,
    14	                     int,
    15	                     char *);
    16	void down_chopsticks (int,
    17	                      int);
    18	int food_on_table ();
    19	int get_token ();
    20	void return_token ();
    21	
    22	pthread_mutex_t chopstick[PHILOS];
    23	pthread_t philo[PHILOS];
    24	pthread_mutex_t food_lock;
    25	pthread_mutex_t num_can_eat_lock;
    26	int sleep_seconds = 0;
    27	uint32_t num_can_eat = PHILOS - 1;
    28	
    29	
    30	int
    31	main (int argn,
    32	      char **argv)
    33	{
    34	    int i;
    35	
    36	    pthread_mutex_init (&food_lock, NULL);
    37	    pthread_mutex_init (&num_can_eat_lock, NULL);
    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	    /* 箸が一巡した */
    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	    int successful = 0;
   116	
   117	    while (!successful) {
   118	        pthread_mutex_lock (&num_can_eat_lock);
   119	        if (num_can_eat > 0) {
   120	            num_can_eat--;
   121	            successful = 1;
   122	        }
   123	        else {
   124	            successful = 0;
   125	        }
   126	        pthread_mutex_unlock (&num_can_eat_lock);
   127	    }
   128	}
   129	
   130	void
   131	return_token ()
   132	{
   133	    pthread_mutex_lock (&num_can_eat_lock);
   134	    num_can_eat++;
   135	    pthread_mutex_unlock (&num_can_eat_lock);
   136	}

この修正されたバージョンの食事する哲学者プログラムをコンパイルして、何回か実行してみてください。このトークンを用いたシステムは、箸を使用する人数を制限することで、実および潜在的デッドロックを回避します。

3.5.1.1 誤検出の報告

前述のトークンを用いたシステムを使用したシステムの実装で、潜在的デッドロックが存在しないにもかかわらず、スレッドアナライザからは潜在的デッドロックが報告されます。これは誤検出です。潜在的デッドロックの詳細情報を示す次のスクリーンショットを検出してみましょう。

スレッドアナライザウィンドウに表示されたスレッド 2 のデッドロック情報のスクリーンショット

チェーンの最初のスレッド (スレッド #2) を選択し、「デュアルソース」タブをクリックして、スレッド #2 のロック保持が発生したアドレス 0x215a8 に対応するソースコード位置と、ロック要求が発生したアドレス 0x215c0 に対応するソースコード位置を確認します。次のスクリーンショットは、スレッド #2 の「デュアルソース」タブを示しています。

スレッドアナライザの「デュアルソース」タブのスクリーンショット - 潜在的デッドロックの例

din_philo_fix1.c 内の get_token() 関数が while ループを使用して、スレッドの同期を取っています。トークンの獲得に成功しない限り (num_can_eat がゼロより大きくなったときに獲得)、スレッドが while ループを離れることはありません。この while ループは、同時に食事をする人を 4 人に制限します。しかし、while ループによって実装されている同期機構は、スレッドアナライザによって認識されません。スレッドアナライザは 5 人の哲学者全員が同時に箸をとって食事をしようとするとみなすため、潜在的デッドロックを報告します。次の節では、スレッドアナライザが認識する同期機構を使用することによって同時に食事をする人数を制限する方法を詳しく説明します。