C++ 等待同一信号量的多个线程

C++ 等待同一信号量的多个线程,c++,windows,multithreading,visual-c++,semaphore,C++,Windows,Multithreading,Visual C++,Semaphore,假设有5个线程在等待信号量 CreateSemaphore(sem_bridgempty,0,1,INFINITE); WaitForSingleObject(sem_bridgempty, INFINITE); 现在当sem_bridgeempty发出信号时,5个线程中的一个将被唤醒,其余线程将再次等待sem_bridgeempty发出信号。我在这里吗 我正在实施单车道桥梁问题,即一次只能有车辆从一个方向行驶。此外,桥梁的通行能力固定为5。到目前为止,我所做的是 unsigned WINAP

假设有5个线程在等待信号量

CreateSemaphore(sem_bridgempty,0,1,INFINITE);
WaitForSingleObject(sem_bridgempty, INFINITE);
现在当sem_bridgeempty发出信号时,5个线程中的一个将被唤醒,其余线程将再次等待
sem_bridgeempty
发出信号。我在这里吗

我正在实施单车道桥梁问题,即一次只能有车辆从一个方向行驶。此外,桥梁的通行能力固定为5。到目前为止,我所做的是

unsigned WINAPI enter(void *param)
{
    int direction = *((int *)param);
    while (1)
    {
        WaitForSingleObject(sem_bridgecount, INFINITE);
        WaitForSingleObject(mut_mutex, INFINITE);
        if (curr_direction == -1 || direction == curr_direction)
        {
            curr_direction = direction;
            cars_count++;
            std::cout << "Car with direction " << direction << " entered " << GetCurrentThreadId() << std::endl;
            ReleaseMutex(mut_mutex);
            break;
        }
        else
        {
            ReleaseMutex(mut_mutex);
            WaitForSingleObject(sem_bridgempty, INFINITE);
        }
    }
    Sleep(5000);
    exit1(NULL);
    return 0;
}

 unsigned WINAPI exit1(void *param)
{   
    WaitForSingleObject(mut_mutex, INFINITE);

    cars_count--;
    std::cout << "A Car exited " << GetCurrentThreadId() << std::endl;
    ReleaseSemaphore(sem_bridgecount, 1, NULL);
    if (cars_count == 0)
    {
        curr_direction = -1;
        std::cout << "Bridge is empty " << GetCurrentThreadId() << std::endl;
        ReleaseSemaphore(sem_bridgempty, 1, NULL);
    }
    ReleaseMutex(mut_mutex);
    return 0;
}

int main()
{
    sem_bridgecount = CreateSemaphore(NULL, 5, 5, NULL);
    sem_bridgempty = CreateSemaphore(NULL, 0, 1, NULL); 
    mut_mutex = CreateMutex(NULL, false, NULL);
    //create threads here
}
一辆车正朝着方向1行驶。现在有三个方向为2的输入请求。所有3个请求都将被阻止在
WaitForSingleObject(sem_bridgempty,INFINITE)。现在当桥变空时。三个桥中的一个将被拾取。拾取的一个将再次使桥变为非空。然后,其他两个桥仍将等待桥变空,即使方向相同。
因此,即使桥上有
direction=2
车,其他方向相同的车仍在等待
sem\u bridgempty

我甚至想过使用
sem\u bridgempty
作为事件,而不是
信号灯
(当
cars\u count=0时,在
exit1()中设置事件()
,当第一辆车进入时,在
enter()中设置事件()
)。但仍然没有唤醒所有线程。

最干净的选项是使用临界部分和条件变量

ENTER算法如下所示:

  • 索赔的关键部分
  • 在循环中调用SleepConditionVariableCS,如中所示,直到:
    • 交通方向正确,桥梁通行能力不足,或
    • 桥是空的
  • 更新状态以表示车辆进入驾驶台
  • 释放关键部分
退出算法如下所示:

  • 索赔的关键部分
  • 更新状态以表示您的汽车离开驾驶台
  • 释放关键部分
  • 调用WakeConditionVariable
条件变量可以是一个整数,其大小表示桥上的车辆数量,其符号表示行驶方向


如果你想避免条件变量,我能想出的最简单的解决方案需要一个关键部分和三个自动重置事件:一个用于每个行驶方向,另一个用于指示桥梁为空。您还需要一个表示桥上汽车数量的变量

ENTER算法如下所示:

  • 使用WaitForMultipleObjects,声明对应于您的行驶方向的事件或对应于桥为空的事件,以最先可用的为准
  • 进入临界区
  • 增加计数以表示车辆进入驾驶台
  • 如果计数未满,则设置代表您行驶方向的事件
  • 离开临界区
退出算法如下所示:

  • 进入临界区
  • 减少计数以表示您的汽车离开驾驶台
  • 如果计数为零,则设置指示网桥为空的事件
  • 如果计数不为零,则设置与您的行驶方向相对应的事件
  • 释放关键部分

    • 需要创建与任务最对应的对象。在当前任务中-我们有两个队列-在两个方向上。这两个队列在意义上都是FIFO。我们需要能够准确地计算队列中的条目数,而不仅仅是一个或所有条目。windows信号灯与此完全对应。这是FIFO队列,通过调用
      releaseSamphore
      我们可以精确地设置要唤醒的线程(条目)数量-这是api lReleaseCount的第二个参数。在事件或条件变量的情况下,我们只能唤醒单个或所有服务员

      您的错误不在于您选择了信号灯-这是此任务的最佳选择。你错了,你选择它的本质是错误的——sem_bridgecount,sem_bridgempty——这根本不是按感觉排队。两个方向有两个信号灯-
      HANDLE\hSemaphore[2]-每个方向一个信号量-将其创建为
      \u hSemaphore[0]=CreateSemaphore(0,0,MAXLONG,0)
      -初始计数为0(!),最大计数不受限制(但可以选择任何值>=5)。当汽车试图进入方向驾驶台,但无法进入时,因为现在另一个方向处于活动状态或驾驶台上没有可用空间-它必须等待信号灯(在FIFO队列中)
      \u hSemaphore[方向]
      。当车辆离开驾驶台时,他需要检查驾驶台上的当前情况,并在一些车辆计数(n)(不是全部或单个)上唤醒一个或另一个方向,因此调用
      releaseSamphore(_hSemaphore[direction],n,0)

      一般而言:

      void enter(int direction)
      {
        EnterCriticalSection(..);
        BOOL IsNeedWait = fn(direction);
        LeaveCriticalSection(..);
        if (IsNeedWait) WaitForSingleObject(_hSemaphore[direction], INFINITE)
      }
      

      请注意,在每辆进入-轿厢中,仅一次进入关键区段,等待后,仅进入驾驶台,无需再次进入cs并检查条件。这是因为我们可以准确地计算车辆数量(不是单个或全部)和
      出口的方向
      ——并且只唤醒必须进入驾驶台的车辆,如果使用事件或条件变量,这将是不可能的

      尽管有条件变量和CS的解决方案是可能的,但我认为它不是最好的,因为: 在
      SleepConditionVariableCS
      中等待后执行线程-再次输入绝对不需要的cs 我们只需要或唤醒一辆车,通过
      WakeConditionVariable
      当多辆车真正进入驾驶台时,或通过
      WakeAllConditionVariable
      但是在这种情况下,多个并发线程再次尝试进入同一个cs,只有一个线程将成为赢家,另一个线程将在这里等待 等待线程的数量可能超过网桥上的最大位置(在您的情况下为5个),并且一些线程需要在循环中再次开始等待。 如果正确使用信号量,所有这些都可以避免

      CreateSemaphore(sem_bridgempty,0,1,INFINITE);
      WaitForSingleObject(sem_bridgempty, INFINITE);
      
      全工作机具
      void exit(int direction)
      {
        EnterCriticalSection(..);
        direction = calc_new(direction);
        if (int WakeCount = calc_wake_count(direction))
        {
          ReleaseSemaphore(_hSemaphore[direction], WakeCount, 0);
        }
        LeaveCriticalSection(..);
      }