我如何防止我的程序陷入僵局但仍然保持互斥? (信号量)

how do i prevent my program from getting deadlock but still keep mutual exclusion? (semaphores)

目前正在使用信号量和线程处理操作系统 class 的家庭作业问题。非常困惑,想知道这里是否有人可以提供帮助。我到目前为止的代码:

01 travelToShop();    //puts thread to sleep between 1 and 1000 cycles
02 s1.acquire();      //lock semaphore, keeps mutual exclusion so no other
03 //thread writes to variable while another one is trying to do the same
04
05 if (numofcust==5){    //cant have more then 5 customers in a "shop"
06  s2.acquire();     //lock the shop, wait until shop reopens
07 }
08 numofcust++;          //increases variable telling class how many in shop
09 arriveAtShop();       //print statement saying ive arrived, if i arrived
10 //im technically in shop
11
12 s1.release();      //done writing to numofcust
13 sittingInShop();  //puts thread to sleep between 1 and 1000 cycles
14 //simulating having coffee
15
16 s1.acquire();     //refer to last "s1.acquire()"
17 numofcust--;      //simulating leaving the shop
18 if (numofcust==0){ //if shop empty
19 s2.release();     //open the shop
20 }
21 leaveShop();
22 s1.release();    //refer to last "s1.release()"    

我知道问题出在第 6 行和第 12 行。一旦店里有 5 位顾客,其余顾客就必须等待(第 6 行)。因为我必须等待,第一个持有 s1 的人持有信号量,另一个客户必须获得该信号量才能离开(因此没有线程可以释放线程正在等待的锁以释放该线程有人离开的锁。

我尝试过以下操作:

05 if (numofcust==5){    //cant have more then 5 customers in a "shop"
06 s1.release(); //release the lock so others can write to numofcust and read
07  s2.acquire();     //lock the shop, wait until shop reopens
08  s1.acquire();    //reacquire the lock so i can write to numofcust again
09 }

但后来我打破了互斥

我如何在没有锁的情况下没有人可以写入 numofcust 的情况下保持互斥,但又要防止一个线程持有 numofcust 的锁因为等待商店开门而导致的死锁?

编辑:如果店里有5个顾客,店外的所有顾客都必须等到他们都离开,但是如果顾客少于5个,他们可以随便进去

您不需要两个信号量。 Semaphore 对象可以拥有可变数量的许可。只要有许可,就可以多次获取信号量。信号量只有在获得所有许可后才会阻塞。如果许可随后被释放,则阻塞的线程可以唤醒并获取新释放的许可。

许可不属于线程。任何线程都可以释放任意数量的许可。这为您在发放许可证时提供了很大的灵活性。

离开时,您需要根据已进入人员的高水位线改变许可证的发放方式。当 high watermark 小于 5 时立即释放。当 high water mark 为 5 时,等到最后一个人离开,然后释放所有许可。这意味着根据信号量许可证单独计算商店中的人数,并在达到 5 时设置一个标志。计数和标志一次只需要由一个线程更新和检查。

@MattChampion 的回答很好,但我只想指出,解决问题的方法往往不止一种。

如果您无法使用 Semaphore class,另一种解决此问题的方法是使用 BlockingQueue:

在程序开始时将五个"tokens"放入阻塞队列。令牌可以是任何对象。 (例如,Object token = new Object();)然后,确保每个客户线程在进入商店之前从队列中取出令牌,并在离开商店后returns将令牌放入队列中。

如果一个线程在队列为空时(店内有五个顾客)尝试取令牌,它将被阻塞,直到有令牌可用。

阻塞队列是一种强大的抽象,除了作为将对象从一个线程传递到另一个线程的管道之外,还可以用于很多方面。