Oracle® Solaris Studio 12.4: スレッドアナライザユーザーズガイド

印刷ビューの終了

更新: 2014 年 12 月
 
 

トークンの代替システム

次のリストには、トークンのシステムを実装する代替方法が示されています。この実装方法でも 4 つのトークンを使用するので、同時に 4 人しか食事しようとしません。ただし、この実装方法は、sem_wait()sem_post() セマフォールーチンを使用して、食事する哲学者の人数を制限します。このバージョンのソースファイルは din_philo_fix2.c と呼ばれます。


ヒント  -  サンプルアプリケーションをダウンロードした場合、din_philo_fix2.c ファイルを SolarisStudioSampleApplications/ThreadAnalyzer/din_philo ディレクトリからコピーできます。

次のリストでは、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 は、哲学者の人数より 1 少ない 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() の呼び出しを認識し、哲学者全員が同時に食事しようとしているわけではないと判断します。


注 -  適切なセマフォールーチンとリンクするように、–lrt を付けて din_philo_fix2.c をコンパイルする必要があります。

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 で報告されないデッドロック

image:デッドロックを表示しない「スレッドアナライザ」ウィンドウのスクリーンショット。

スレッドアナライザが認識するスレッドおよびメモリー割り当て API のリストについては、Appendix A, スレッドアナライザで認識される APIを参照してください。