参考https://blog.youkuaiyun.com/booksyhay/article/details/82769005
在如图中所示的程序中,while语句是一个无限循环; 换句话说,一旦线程离开临界区,它就会循环到顶部并尝试再次获取互斥锁。
想象一下,线程A获取互斥锁,线程B和C等待。 当A离开时,B进入,但在B离开之前,A循环回来加入到队列中和C一起排队. B离开时,无法保证C接下来会执行。 实际上,如果接下来是A,并且B加入队列,那么我们将回到起始点,可以永远重复该循环。 C饿死。
这种模式的存在证明了互斥体容易受到饿死的影响。
解决方案:
使用两个十字转门在临界区之前创建两个等候室。 该机制分两个阶段进行。 在第一阶段期间,第一个旋转门打开而第二个旋转门关闭,因此线程在第二个房间中积聚。 在第二阶段,第一个旋转门关闭,因此没有新线程可以进入,第二个旋转门打开,因此现有线程可以到达临界区。
虽然等候室中可能有任意数量的线程,但这里的每个线程都确保在未来的其他线程到达之前进入临界区。
初始化:
room1和room2跟踪等候室中的线程数。 mutex有助于保护计数器。 t1和t2是十字转门。
osSemaphoreId_t sem_t1;
sem_t1 = osSemaphoreNew(1, 1, NULL); /*t1旋转门初始化打开*/
osSemaphoreId_t sem_t2;
sem_t2 = osSemaphoreNew(1, 0, NULL);/*t2旋转门初始化关闭*/
osSemaphoreId_t sem_mutex;
sem_mutex = osSemaphoreNew(1, 1, NULL);
int room1 = 0;
int room2 = 0;
线程:
在进入临界区之前,线程必须通过两个十字转门。这些十字转门将代码划分为三个“房间”。 第28行是1号房间,第618行是2号线,其余的是3号房间。不严格地说,计数器room1和room2跟踪每个房间的线程数。
计数器room1以通常的方式受互斥锁mutex保护,但是room2的保护任务分配在t1和t2中。同样,对临界区进行独占访问的责任包括t1和t2这两个信号量。为了进入临界区,线程必须持有其中的一个,但不能同时持有两个。然后,在退出之前,它会放弃它所持有的任何一个。
while(1)
{
osSemaphoreAcquire(sem_mutex, osWaitForever);
{
room1++;
printf("enter room1\r\n");
}
osSemaphoreRelease(sem_mutex);
osDelay(10); /*等待别的线程进入房间*/
osSemaphoreAcquire(sem_t1, osWaitForever); /*关闭t1*/
{
room2++;
printf("enter room2\r\n");
osDelay(10); /*等待别的线程进入房间*/
osSemaphoreAcquire(sem_mutex, osWaitForever);
room1--;
if(room1 == 0)
{
printf("open turnstile 2\r\n");
osSemaphoreRelease(sem_mutex);
osSemaphoreRelease(sem_t2); /*打开t2*/
}
else
{
osSemaphoreRelease(sem_mutex);
osSemaphoreRelease(sem_t1); /*打开t1*/
}
}
osSemaphoreAcquire(sem_t2, osWaitForever); /*关闭t2*/
{
room2--;
printf("enter room3\r\n");
//critical_point();
if(room2 == 0)
{
printf("open turnstile 1\r\n");
osSemaphoreRelease(sem_t1); /*打开t1*/
}
else
{
osSemaphoreRelease(sem_t2); /*打开t2*/
}
}
}
结论: