多个线程等待相同的信号量

时间:2017-07-16 15:22:11

标签: c++ windows multithreading visual-c++ semaphore

假设有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
}

考虑以下部分

    else
    {
        ReleaseMutex(mut_mutex);
        WaitForSingleObject(sem_bridgempty, INFINITE);

一辆汽车正朝着方向1.现在有三个进入请求方向2.所有3将被阻挡在WaitForSingleObject(sem_bridgempty, INFINITE);。现在当大桥空了。三个中的一个将被拿起。一个拾起将再次使桥非空。然后其他两个仍将等待桥空,即使方向相同。 因此,即使桥上有direction=2车,其他方向相同的车仍在等待sem_bridgempty。 我甚至考虑过使用sem_bridgempty作为事件而不是semaphoreexit1()中的{setevent()cars_count=0resetevent() enter()汽车进入)。但仍然所有线程都没有醒来。

2 个答案:

答案 0 :(得分:0)

最干净的选择是使用临界区和条件变量。

ENTER算法如下所示:

  • 声明关键部分。
  • 在循环中调用SleepConditionVariableCS,如Using Condition Variables所示,直到:
    • 交通正朝着正确的方向行驶,桥梁还有剩余容量,或
    • 桥是空的。
  • 更新状态以代表您的汽车进入桥梁。
  • 发布关键部分。

EXIT算法如下所示:

  • 声明关键部分。
  • 更新状态以代表您的汽车离开桥梁。
  • 发布关键部分。
  • 致电WakeConditionVariable。

条件变量可以是一个整数,其大小代表桥上汽车的数量,其符号代表行驶方向。

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

ENTER算法如下所示:

  • 使用WaitForMultipleObjects,声明与您的旅行方向相对应的事件或与桥梁相对应的事件为空(以先到者为准)。
  • 输入关键部分。
  • 增加计数以代表您的汽车进入桥梁。
  • 如果计数不足,请设置表示旅行方向的事件。
  • 离开关键部分。

EXIT算法如下所示:

  • 输入关键部分。
  • 减少计数以代表您的汽车离开桥梁。
  • 如果计数为零,请设置指示桥为空的事件。
  • 如果计数非零,请设置与您的旅行方向相对应的事件。
  • 发布关键部分。

答案 1 :(得分:0)

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

你的错误不在于你选择信号量 - 这是这项任务的最佳选择。你误认为你选择了错误的本质 - sem_bridgecount,sem_bridgempty - 这根本就不是排队的。你有2个方向的2个信号量 - HANDLE _hSemaphore[2]; - 每个方向一个信号量 - 创建为_hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) - 初始计数为0(!),最大计数为无限(但可以选择任何值&gt; = 5)。当汽车尝试进入以方向桥接而不能,因为现在另一个方向处于活动状态或桥上没有可用空间 - 它必须等待信号量(在FIFO队列中){ {1}}。当汽车从桥上退出时 - 他需要检查桥上的当前情况并在一些确切的汽车数量( n )(不是全部或单个)上唤醒一个或另一个方向 - 所以致电_hSemaphore[direction]

一般来说:

ReleaseSemaphore(_hSemaphore[direction], n, 0);

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

请注意,在每次进入汽车时,只有一次进入CriticalSection,等待void exit(int direction) { EnterCriticalSection(..); direction = calc_new(direction); if (int WakeCount = calc_wake_count(direction)) { ReleaseSemaphore(_hSemaphore[direction], WakeCount, 0); } LeaveCriticalSection(..); } 之后,只需进入桥接状态,无需再次进入cs并检查条件。这是因为我们可以准确计算_hSemaphore[direction]中的汽车数量(不是单一或全部)和方向 - 并且只唤醒必须进入桥梁的汽车,如果使用事件或条件变量,这将是不可能的

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

,这一切都可以避免

完整的工作实施here