硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2999|回复: 4
收起左侧

[RL-RTX] 使用RTX5 信号量 解决哲学家就餐问题

[复制链接]

32

主题

295

回帖

391

积分

高级会员

积分
391
发表于 2020-2-25 21:01:06 | 显示全部楼层 |阅读模式
1965 年,Dijkstra 提出并解决了一个他称之为哲学家 就餐的同步问题。从那时起,每个发明新的同步原语的人都希望通过解决哲 学家就餐问题来
展示其同步原语的精妙之处。这个问题可以简单地描述如 下:五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由 于通心粉很滑,
所以需要两把叉子才能夹住。相邻两个盘子之间放有一把 叉子,餐桌如图 1 示。
QQ图片20200225205642.png
哲学家的生活中有两种交替活动时段:即吃饭和思考(这只是一种抽象,即对哲学家而言其他活动都无关紧要)。
当一个哲学家觉得饿了时, 他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。
如 果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。
关键 问题是:能为每一个哲学家写一段描述其行为的程序,且决不会死锁吗?

声明:以上文字和图片出自《Operating Systems Design and
Implementation》 By Andrew S. Tanenbaum。 该实验是对哲学家就餐问题的模拟实现。
《Operating Systems  Design and  Implementation》中的哲学家就餐问题的就解决方案。
如果读者想要对这个问题进行深入的理解请参考此书 2.3.1  节。
每位哲学家一共有三种状态,分别为思考(THINKING),饥饿(HUNGRY),和进餐(EATING)。
当哲学家从思考中醒来则进入到饥饿状态,他会试图 获取餐叉。当获取到两把叉子,则进入进餐状态(EATING)使用一个数组 phd_state[N],来跟踪每位哲学家的状态,在本实验中, N 为 5。0~4 代表哲学家

一个哲学家只有当两个邻居都没有进餐是才允许进入到进餐状态。哲学家i的两个邻居由 LEFT_PHD(i)和 RIGHT_PHD(i)。
即若 i 为 2,则 LEFT_PHD 为 1,RIGHT_PHD 为 3。该程序使用了一个信号量数组,每个信号量对应一位哲学家,这样在 所需的叉子被占用时,想进餐的哲学家就被阻塞。
初始化信号量数组,以及创建 5个哲学家线程。
注意,程序中哲学家状态数组的操作为临界区,需要互斥, 这里使用二值信号量 sem_lock 实现(用互斥也可以)。
5 个哲学家线程具有相同的优先级和 时间片。
  1. /*
  2. * 程序清单:哲学家就餐问题
  3. * 参考《Operating Systems Design and Implementation》 By Andrew S. Tanenbaum
  4. */
  5. #include "includes.h"


  6. #define N 5                             /* 定义哲学家的数目5 */

  7. osSemaphoreId_t sem[N];                     /* 每位哲学家一个信号量 */

  8. osSemaphoreId_t sem_lock;                   /* 定义二值信号量实现临界区互斥 */


  9. /* 哲学家围绕一个圆桌 */
  10. #define LEFT_PHD(i)   ((i+N-1)%N)         /* 哲学家i左边的哲学家 */
  11. #define RIGHT_PHD(i)  ((i+1)%N)           /* 哲学家i右边的哲学家 */

  12. /* 定义使用枚举类型表示哲学家状态*/
  13. enum _phd_state
  14. {                             
  15.         THINKING = 0,         
  16.         HUNGRY,                  
  17.         EATING,                         
  18. } phd_state[N];                 /* 定义哲学家状态数组 */

  19. const char * status_string[N] =
  20. {
  21.     "thinking",
  22.     "hungry",
  23.     "eating",
  24. };

  25. /* 哲学家处理线程ID */
  26. osThreadId_t tid_Thread01;                        // thread id
  27. osThreadId_t tid_Thread02;                        // thread id
  28. osThreadId_t tid_Thread03;                        // thread id
  29. osThreadId_t tid_Thread04;                        // thread id
  30. osThreadId_t tid_Thread05;                        // thread id

  31. const osThreadAttr_t phd01_thread_Attr =
  32. {
  33.         .name = "phd01_Thread",
  34.         .attr_bits = osThreadDetached,
  35.         .priority = osPriorityLow1,
  36.         .stack_size = 512,
  37. };

  38. const osThreadAttr_t phd02_thread_Attr =
  39. {
  40.         .name = "phd02_Thread",
  41.         .attr_bits = osThreadDetached,
  42.         .priority = osPriorityLow1,
  43.         .stack_size = 512,
  44. };

  45. const osThreadAttr_t phd03_thread_Attr =
  46. {
  47.         .name = "phd03_Thread",
  48.         .attr_bits = osThreadDetached,
  49.         .priority = osPriorityLow1,
  50.         .stack_size = 512,
  51. };

  52. const osThreadAttr_t phd04_thread_Attr =
  53. {
  54.         .name = "phd04_Thread",
  55.         .attr_bits = osThreadDetached,
  56.         .priority = osPriorityLow1,
  57.         .stack_size = 512,
  58. };

  59. const osThreadAttr_t phd05_thread_Attr =
  60. {
  61.         .name = "phd05_Thread",
  62.         .attr_bits = osThreadDetached,
  63.         .priority = osPriorityLow1,
  64.         .stack_size = 512,
  65. };


  66. /* 记录哲学家线程执行 进餐概率大致相同 */
  67. uint16_t usEatNum[N];                 



  68. /* 尝试持有叉子吃饭 */
  69. void take_forks(int i)
  70. {
  71.     /* 进入临界区通过一个二值信号量实现 */
  72.     osSemaphoreAcquire (&sem_lock, osWaitForever);
  73.        
  74.     phd_state[i] = HUNGRY;        /* 处于饥饿状态   -- 线程就绪等待调度       */

  75.         /* 隔壁个人均不是吃饭状态 同一时间只允许一个人处于吃饭状态 */
  76.         if(phd_state[LEFT_PHD(i)] != EATING &&
  77.         phd_state[RIGHT_PHD(i)] != EATING)
  78.     {
  79.         phd_state[i] = EATING;

  80.         /* 可以得到叉子,发布对应哲学家线程信号量 */
  81.         osSemaphoreRelease(&sem[i]);
  82.     }
  83.                
  84.     /* 退出临界区*/
  85.     osSemaphoreRelease(&sem_lock);

  86.     /* 如果不处于EATING状态则阻塞哲学家 */
  87.     osSemaphoreAcquire (&sem[i], osWaitForever);                /* 尝试持有信号量 */
  88. }

  89. /* 放下叉子 */
  90. void put_forks(int i)
  91. {
  92.     /* 进入临界区*/
  93.     osSemaphoreAcquire (&sem_lock, osWaitForever);

  94.         /* 切换到思考状态 */
  95.     phd_state[i] = THINKING;        /* 处于思考状态   -- 线程挂起       */

  96.     /* 退出临界区*/
  97.     osSemaphoreRelease(&sem_lock);
  98. }


  99. /* 哲学家线程 */
  100. void phd_thread_entry(void* parameter)
  101. {
  102.     int i;
  103.         int j;

  104.     i = (int)parameter;
  105.        
  106.     printf("phd %i starts...\r\n", i);
  107.        
  108.     while(1)
  109.     {
  110.         /* thinking */   
  111.         osDelay(OS_TICK_FREQ);
  112.         //printf("phd %d is %s\r\n", i, status_string[ phd_state[i] ] );   

  113.         /* take forks */
  114.         take_forks(i);

  115.                 usEatNum[i]++;

  116.                 if(i==4)
  117.                 {
  118.                         if(usEatNum[4]%20 == 0)
  119.                         {
  120.                                 printf("phd %i starts...\r\n", i);
  121.                                 for(j=0; j<N; j++)
  122.                                 {
  123.                                         printf("phd %d eatnum %d\r\n", j, usEatNum[i] );   
  124.                                 }
  125.                                 printf("\r\n");
  126.                         }
  127.                 }
  128.         /* eating */
  129.         //printf("phd %d is %s\r\n", i, status_string[ phd_state[i] ] );   
  130.         osDelay(OS_TICK_FREQ);

  131.         /* put forks */
  132.         put_forks(i);
  133.     }
  134. }




  135. int rtx_application_init(void)
  136. {
  137.     /* 初始化信号量 */
  138.         uint8_t i = 0;
  139.        
  140.         sem_lock = osSemaphoreNew(2U, 2U, NULL);
  141.         if (sem_lock == NULL)
  142.         {
  143.                 goto _error; // Semaphore object not created, handle failure
  144.         }

  145.         for (i=0; i<5; i++)
  146.         {
  147.                   sem[i] = osSemaphoreNew(1U, 1U, NULL);                /* 信号量初始值设置成1 */
  148.                   if (sem[i] == NULL)
  149.                   {
  150.                         goto _error;
  151.                 }
  152.         }

  153.         /* 创建5个哲学家处理线程 */
  154.         tid_Thread01 = osThreadNew(phd_thread_entry, (void *)0, &phd01_thread_Attr);
  155.         if (tid_Thread01  == NULL)
  156.         {
  157.                 return(-2);
  158.         }

  159.         tid_Thread02 = osThreadNew(phd_thread_entry, (void *)1, &phd02_thread_Attr);
  160.         if (tid_Thread02  == NULL)
  161.         {
  162.                 return(-2);
  163.         }

  164.         tid_Thread03 = osThreadNew(phd_thread_entry, (void *)2, &phd03_thread_Attr);
  165.         if (tid_Thread03 == NULL)
  166.         {
  167.                 return(-2);
  168.         }

  169.         tid_Thread04 = osThreadNew(phd_thread_entry, (void *)3, &phd04_thread_Attr);
  170.         if (tid_Thread04   == NULL)
  171.         {
  172.                 return(-2);
  173.         }

  174.         tid_Thread05 = osThreadNew(phd_thread_entry, (void *)4, &phd05_thread_Attr);
  175.         if (tid_Thread05   == NULL)
  176.         {
  177.                 return(-2);
  178.         }
  179.        
  180.        
  181.     return 0;
  182.        
  183. _error:
  184.     printf("init semaphore failed.\r\n");
  185.     return -1;

  186. }
  187. 程序执行结果:5 个哲学家线程完全一致,运行一段时间后,他们进餐的
复制代码



QQ图片20200225205941.png

评分

参与人数 1金币 +100 收起 理由
eric2013 + 100 赞一个!

查看全部评分

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106660
QQ
发表于 2020-2-26 00:22:57 | 显示全部楼层
非常感谢楼主分享。
回复

使用道具 举报

0

主题

3

回帖

3

积分

新手上路

积分
3
发表于 2020-2-27 11:16:54 | 显示全部楼层
感谢楼主分享
回复

使用道具 举报

0

主题

16

回帖

16

积分

新手上路

积分
16
发表于 2020-5-20 09:09:30 | 显示全部楼层
感谢分享~
回复

使用道具 举报

3

主题

1222

回帖

1231

积分

至尊会员

积分
1231
发表于 2020-5-20 11:03:59 | 显示全部楼层
感谢楼主分享,
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-29 01:12 , Processed in 0.174321 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表