【问题标题】:Traffic Intersection Simulation using Multi Threading Algorithm使用多线程算法的交通路口仿真
【发布时间】:2012-02-22 22:51:12
【问题描述】:

这是一个面试问题,被问到并希望找到一个有效的解决方案。

问题

考虑如图所示的四条道路的交叉点。每条道路都被定义为有一个方向。您将如何解决问题以改善交通状况避免死锁

  1. 交叉口分为四个象限[以黄色显示]。
  2. 汽车从四个方向[0,1,2,3]随机进入路口
  3. 在十字路口,每辆车都可以单步移动。三个可能的动作是
    1. 向左走
    2. 直走
    3. 向右走

例如:

如果汽车从方向 2 进入并想左转。它应该通过 quandrant 2quandrant 1,最后是 quandrant 0

半完整解决方案

用黄色标记的每个象限都有一个与之关联的信号量。 我想象的是一个两阶段协议,其中每辆车都会

  1. 获取它将通过的象限列表
  2. 从最后一个象限开始锁定每个象限
    1. 在上面的示例中,来自方向 2 的汽车将锁定 象限 0象限 1,然后锁定 象限 2 >.
  3. 穿过十字路口
  4. 释放获取的锁。
    1. 锁以相同的顺序释放。 象限 0象限 1象限 2

但是,上述解决方案并不是最优的,因为它会导致死锁。

我的问题是

  1. 还有什么其他/更好的方法可以实现?
  2. 我可以使用 C 中的信号量来实现吗?
    1. 如果不是,我应该使用哪种同步方法?

更新

  1. 我想要一个解决方案,它允许多辆汽车进入交叉路口,并且仍然避免死锁和碰撞。在整个交叉路口设置一个锁会不够优化。

【问题讨论】:

  • 如果生成每个汽车运动的进程是独立的,则需要信号量。如果您在单个程序中模拟整个事情,则不需要信号量,只需标志就足够了。

标签: multithreading algorithm synchronization deadlock semaphore


【解决方案1】:

您的解决方案(如第 2 步中所建议的)不能避免死锁。

考虑这样一种情况,即从四条街道有汽车想要向左转。然后所有的汽车开始锁定不同的 cuadrants:

从 0 方向,它锁定 cuadrant 2。 从方向 1,它锁定 cuadrant 3。 从方向 2,它锁定 cuadrant 0。 从方向 3,它锁定 cuadrant 1。 -- 死锁 --

您可以通过四个方向共享一个互斥锁来避免死锁。

【讨论】:

  • 为所有象限设置一个互斥锁是最简单的,但也是一个微不足道且优化程度最低的解决方案,它会严重地在交叉路口造成瓶颈。抱歉,我想要两辆车可以进入十字路口但仍能避免碰撞的东西。我将更新问题以包含此内容!
  • 我同意 Gray 建议的解决方案更自然,对于面试问题是一个很好的答案,但它更有效吗?要找出答案,您需要分析您的流量模式:如果 95% 的流量来自 2 并想去 3,而 5% 来自 0 并想去 3 会发生什么?该解决方案退化为具有大共享互斥锁的解决方案。公平吗?否:目标街道的 cuadrant 数字较小,比 cuadrant 数字较高的街道阻塞时间更长,因此从街道 3 右转到 2 的车辆将比从 0 到 3 的车辆获得更高的优先级。
【解决方案2】:

我能够使用以下逻辑在 C 中实现一个解决方案(实际实现对每个象限都使用了信号量)。

我在问题中提到的解决方案部分正确,但是由于制作锁的问题,我遇到了死锁。 我能够通过为锁分配优先级来解决这个问题。 通过首先锁定最高优先级的锁来获得锁。 以下是每辆车使用的伪代码算法:

假设:

  1. 每个象限都有一个与之关联的锁。
  2. 每个锁都有一个关联的优先级。优先级如下

优先级(Lock_0)
*其中 Lock_x 是象限 x 的锁

算法

  1. 每辆车都有将通过的象限列表

  2. 从具有最高优先级的象限开始锁定每个象限。

    例如:在上面的例子中,从方向 2 左转的汽车将锁定 象限 2象限 0,然后 象限 1 .

  3. 穿过十字路口

  4. 释放获取的锁。

    例如:在上面的示例中,锁以相同的顺序释放。 象限 2象限 1象限 0

【讨论】:

  • 一个优化是我很确定你可以在离开象限后解锁一个象限。您无需等待离开十字路口即可全部解锁。
【解决方案3】:

我假设您的“最后一个象限”锁定顺序是它通过交叉路口时看到的最后一个?

我认为您可以更改您的算法以始终按数字顺序锁定象限 - 如果任何象限已被锁定,则解锁并重试。汽车必须先锁定其完整路径,然后才能驶过十字路口并解锁。例如,如果一辆车想从路径 1 到 2,它会先锁定第 0 象限,然后再锁定 3。如果它想从路径 3 到 1,那么它会先锁定 2,然后再锁定 3。

我认为这解决了死锁问题,因为每个线程都以相同的顺序锁定。当有人锁定 3 0 1 和有人锁定 1 2 3 时,就会使用死锁。

所以完整的任务列表是:

  1. 它尝试按象限数字顺序(0、1、2、3)锁定其路径。如果这是 C,那么我会使用 `pthread_mutex_trylock() 并为每个象限加一个锁。
  2. 如果它发现一个象限被锁定,它会解锁它锁定的象限,随机休眠一段时间,然后重新开始。
  3. 如果它锁定了它的完整路径,那么它就可以遍历该路径。
  4. 一旦穿过象限,就可以安全解锁。无需等到从另一边出去就可以解锁。

正如您所提到的,它不能做的是锁定它的第一个象限,进入该象限,然后尝试锁定路径中的下一个象限。在这种情况下,这将导致死锁或僵局。

【讨论】:

  • 这里的每辆车在进入任何一个象限之前锁定所有将进入的象限。因此,两个锁相阶段。如果我想检查象限是否被锁定并随机休眠,那么适当的锁定原语应该使用什么,然后继续执行相同操作直到我获得锁定。这种锁定和释放锁也不会导致汽车饥饿吗?
  • 如果您使用的是 C,那么我会使用 pthread_mutex_trylock。同样,您必须在进入交叉路口之前锁定所有 个象限。如果任何 trylock 失败,那么您解锁所有锁,休眠,然后重试。如果随机睡眠对汽车起作用,那么它可能会等待比它应该等待的时间更长,但实际上它是最佳的。我不明白“两相锁定”的评论。
【解决方案4】:

您实际上可能不需要信号量来实现解决方案。 问题不在于资源控制(不需要锁定象限) 是关于资源调度的..

让我们假设运动/时间发生在离散的步骤中

要知道何时开始移动您的汽车,您必须预测该象限是否会在您将来需要它时使用。为此,当每辆车进入交叉路口时,它应该描述其意图。 . 使用一系列步骤.. 示例:

让我们有一个队列,每个象限有 1 行

 T 1 2 3 4 5 6 7 8 9 
 0|
 1|
 2|
 3|

当 1 号车从底部 (2) 出来时,他将使用(象限:2 1 0),因此他保留了象限

 T 1 2 3 4 5 6 7 8 9 
 0|    1
 1|  1 
 2|1
 3|

如果 2 号车在第 2 点从右边来并且想要前进 (Q: 1 0)

 T 1 2 3 4 5 6 7 8 9 
 0|   2*1
 1| 2*1 
 2|1
 3|

我们发生了碰撞,所以每辆车只有在它需要的所有象限都是空的时才会尝试标记意图。如果不是,它会将意图滑动 1 帧并重试。在这种情况下是时间表汽车 2 将是

 T 1 2 3 4 5 6 7 8 9 
 0|    1 2
 1|  1 2
 2|1
 3|

如果没有发生冲突,则可以尽快安排运动,如果 3 号车在时间帧 2 从顶部 (0) 到达并想下到底部 (Q:0 3),他可以立即开始。因为它打算使用的象限都不会在他需要使用时被占用

 T 1 2 3 4 5 6 7 8 9 
 0|  3 1 2
 1|  1 2
 2|1
 3|    3

这是通用方法..对于具体实现会有变化.. 如果你有线程(每辆车一个),你可能需要同步他们对队列的访问..但这只是同步数据访问不是交叉逻辑

如果运动步骤不是离散单位,队列帧将必须代表 X 秒的动画..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 2010-10-15
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多