2017-07-16 56 views
0

假设有5个线程等待信号多个线程等待同一个信号

CreateSemaphore(sem_bridgempty,0,1,INFINITE); 
WaitForSingleObject(sem_bridgempty, INFINITE); 

现在,当sem_bridgeempty发出信号,在5个线程的人会醒来,其余的将再次等待sem_bridgeempty进行通知。我在这里吗?

我实现一个车道桥梁问题,其中可能存在的车辆只能在time.Also从一个方向移动的桥的容量是固定在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.Now将会有三个输入与方向2.所有3将在WaitForSingleObject(sem_bridgempty, INFINITE);。现在被封锁请求时,桥去的三empty.One会拿起来。拿起一个将再次使b即使方向相同,另外两个仍然会等待桥梁变空。 所以即使桥上有direction=2汽车,其他方向相同的汽车仍在等待sem_bridgempty。 我甚至(在exit1()cars_count=0resetevent()enter() SetEvent的()当第一辆车进入)认为使用sem_bridgempty作为一个事件,而不是semaphore的。但仍然是所有线程不会醒来。

+1

有什么特别的理由,你为什么不使用['的std :: condition_variable'(http://en.cppreference.com/ W/CPP /线程/ condition_variable)? – user0042

+0

没有特别的原因。但是我必须使用windows同步函数来做这件事,所以我想我可以尝试'sleepconditionvariablecs()',但是我也必须使用'EnterCriticalSection()'。也可以使用信号量和互斥量来完成?即使我必须使用别的东西,我也对这里最好的方法感兴趣。 – user3819404

+0

问题在选择的解决方案逻辑。你怎么看它不是最好的。尝试[此代码](https://stackoverflow.com/a/45133951/6401656) – RbMm

回答

0

最简洁的选项是使用临界区和条件变量。

按ENTER算法是这样的:

  • 索赔的关键部分。在一个循环中
  • 呼叫SleepConditionVariableCS,如图Using Condition Variables,直到:
    • 流量将会在正确的方向和桥已经离开容量,或
    • 桥是空的。
  • 更新状态来表示您的车进入桥。
  • 释放关键部分。

的EXIT算法是这样的:

  • 索赔的关键部分。
  • 更新状态来表示您的车离开桥。
  • 释放关键部分。
  • 致电WakeConditionVariable。

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


如果你想避免的条件变量,我能想出的最简单的解决方案需要一个关键部分和三个自动重置事件:一个旅行的每个方向,加一,以表明大桥是空的。您还需要一个代表桥上车辆数量的变量。

按ENTER算法是这样的:

  • 使用WaitForMultipleObjects的,要求相应的给您的出行或对应于桥为空时的方向的情况下,无论是使用第一。
  • 输入关键部分。
  • 递增计数以表示您的车进入桥。
  • 如果计数不符合容量,请设置代表您行进方向的事件。
  • 退出关键部分。

的EXIT算法是这样的:

  • 进入临界区。
  • 递减计数以表示您的车离开大桥。
  • 如果计数为零,请设置事件指示桥是空的。
  • 如果计数不为零,请设置与您的行进方向相对应的事件。
  • 释放关键部分。
0

需要创建最对应于任务的对象。在目前的任务中 - 我们有两个队列 - 在两个方向上。这个队列在意义上都是FIFO。而且我们需要有能力唤醒队列中的条目 - 不仅仅是一个或全部。 Windows信号量与此完全相符。这是FIFO队列,通过调用ReleaseSemaphore我们可以精确地设置线程(条目)的数量以唤醒 - 这是api的第二个参数lReleaseCount。如果发生事件或ConditionVariable,我们只能唤醒单个或全部服务员。

你的错误不在于你选择了信号量 - 这是这项任务的最佳选择。你错误地认为你为错误的本质选择了它 - sem_bridgecount,sem_bridgempty - 它根本就不是队列。你需要2个信号量 - HANDLE _hSemaphore[2]; - 每个方向一个信号量 - 创建它为_hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) - 初始值为0(!),最大值不受限制(但可以选择任何值> = 5)。当车试图进入桥接方向而不能,因为现在另一个方向是活动的或没有空闲的桥梁 - 它必须等待信号量(在FIFO队列中)_hSemaphore[direction]。当从桥汽车出口 - 他需要检查桥梁的现状和一些唤醒一个或另一个方向正是汽车数(ñ)(不是全部或单) - 这么叫一般ReleaseSemaphore(_hSemaphore[direction], n, 0);

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

void exit(int direction) 
{ 
    EnterCriticalSection(..); 
    direction = calc_new(direction); 
    if (int WakeCount = calc_wake_count(direction)) 
    { 
    ReleaseSemaphore(_hSemaphore[direction], WakeCount, 0); 
    } 
    LeaveCriticalSection(..); 
} 

注意到,在每一个输入 - 车只有一次进入到CriticalSection的后伺候_hSemaphore[direction]我不要再输入cs并检查条件,只需输入桥接即可。这是因为我们可以准确计算车辆数量(不是单一或全部)和方向exit - 并且只唤醒并且必须进入桥接的车辆,如果使用事件或条件变量,这将是不可能的

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

完整的工作实施here