Oracle® Solaris Studio 12.4:线程分析器用户指南

退出打印视图

更新时间: 2014 年 12 月
 
 

另一种令牌机制

下面列出了令牌机制的另一种实现。该实现仍然使用四个令牌,所以最多有四个就餐者可同时尝试进餐。但是,该实现使用 sem_wait()sem_post() 信号例程限制就餐的哲学家人数。该版本的源文件称为 din_philo_fix2.c


提示  -  下载样例应用程序之后,可以从 SolarisStudioSampleApplications/ThreadAnalyzer/din_philo 目录复制 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() 的调用,并判定并非所有的哲学家都同时尝试进餐。


注 -  必须使用 –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