简体   繁体   English

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

[英]Multiple threads waiting for same semaphore

Suppose there are 5 threads waiting for a semaphore 假设有5个线程在等待信号量

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

Now when sem_bridgeempty is signalled, one of the 5 threads will wake up and rest will again wait for sem_bridgeempty to be signalled.Am i right here? 现在,当发出sem_bridgeempty信号时,这5个线程之一将唤醒,其余线程将再次等待sem_bridgeempty发出信号。我在这里吗?

I am implementing one lane bridge problem where there can be vehicles moving from one direction only at a time.Also the capacity of the bridge is fixed at 5.What i have done so far is 我正在实施一个车道桥问题,即一次只能有一个方向行驶的车辆。而且桥的容量固定为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
}

Consider the below portion 考虑以下部分

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

A car is going in direction 1.Now there are three enter requests with direction 2.All 3 will be blocked at WaitForSingleObject(sem_bridgempty, INFINITE); 一辆汽车向1号方向行驶。现在有3个方向2的输入请求。所有3个将在WaitForSingleObject(sem_bridgempty, INFINITE);处阻塞WaitForSingleObject(sem_bridgempty, INFINITE); .Now when the bridge goes empty.One of the three will be picked up.The one picked up will again make bridge non empty.Then the other two will still wait for the bridge to go empty even though the direction is same. 现在,当桥空了时,三个桥架中的一个将被拾起。一个被拾起的桥将再次使桥不为空。然后,即使方向相同,另外两个桥仍然将等待桥架变空。 So even though there is direction=2 car on the bridge, other cars with the same direction are still waiting for the sem_bridgempty . 因此,即使桥上有direction=2汽车,其他具有相同方向的汽车仍在等待sem_bridgempty I even thought of using sem_bridgempty as an event instead of semaphore (setevent() in exit1() when cars_count=0 and resetevent() in enter() when first car enters).But still all threads don't wake up. 我甚至想用的sem_bridgempty作为一个事件,而不是semaphore (在SetEvent的() exit1()cars_count=0resetevent()enter()当第一辆车进入),但仍然是所有线程不会醒来。

The cleanest option would be to use a critical section and a condition variable. 最干净的选择是使用关键部分和条件变量。

The ENTER algorithm would look like this: ENTER算法如下所示:

  • Claim the critical section. 声明关键部分。
  • Call SleepConditionVariableCS in a loop, as shown in Using Condition Variables , until either: 循环调用SleepConditionVariableCS,如使用条件变量中所示,直到:
    • The traffic is going in the right direction and the bridge has capacity left, or 交通往正确的方向行驶,而桥梁的通行能力还剩下,或者
    • The bridge is empty. 桥是空的。
  • Update the state to represent your car entering the bridge. 更新状态以代表您的汽车进入桥梁。
  • Release the critical section. 释放关键部分。

The EXIT algorithm would look like this: EXIT算法如下所示:

  • Claim the critical section. 声明关键部分。
  • Update the state to represent your car leaving the bridge. 更新状态以表示您的汽车驶离桥梁。
  • Release the critical section. 释放关键部分。
  • Call WakeConditionVariable. 调用WakeConditionVariable。

The condition variable could be an integer whose magnitude represents the number of cars on the bridge and whose sign represents the direction of travel. 条件变量可以是整数,其大小表示桥梁上的轿厢数量,其符号表示行进方向。


If you wanted to avoid condition variables, the simplest solution I could come up with requires one critical section and three auto-reset events: one for each direction of travel, plus one to indicate that the bridge is empty. 如果要避免使用条件变量,那么我能想到的最简单的解决方案需要一个关键部分和三个自动重置事件:一个用于每个行驶方向,另一个用于指示桥是空的。 You will also need a variable representing the number of cars on the bridge. 您还将需要一个变量,代表桥上的汽车数量。

The ENTER algorithm would look like this: ENTER算法如下所示:

  • Using WaitForMultipleObjects, claim the event corresponding to your direction of travel or the event corresponding to the bridge being empty, whichever is available first. 使用WaitForMultipleObjects,声明与您的行进方向相对应的事件或与桥梁相对应的事件为空,以先到者为准。
  • Enter the critical section. 输入关键部分。
  • Increment the count to represent your car entering the bridge. 递增计数以代表您的汽车驶入桥梁。
  • If the count is not at capacity, set the event representing your direction of travel. 如果计数未达到极限,则设置代表您的行进方向的事件。
  • Leave the critical section. 离开关键部分。

The EXIT algorithm would look like this: EXIT算法如下所示:

  • Enter the critical section. 输入关键部分。
  • Decrement the count to represent your car leaving the bridge. 减少计数以代表您的汽车驶离桥梁。
  • If the count is zero, set the event indicating that the bridge is empty. 如果计数为零,则设置指示桥为空的事件。
  • If the count is nonzero, set the event corresponding to your direction of travel. 如果计数不为零,则根据您的行进方向设置事件。
  • Release the critical section. 释放关键部分。

need create objects which most corresponded to task. 需要创建最符合任务的对象。 in current task - we have 2 queues - on both direction. 在当前任务中-我们有2个队列-双向。 both this queue is FIFO by sense. 这两个队列在意义上都是FIFO。 and we need have ability wake exactly count of entries in queue - not only one or all. 并且我们需要具有唤醒队列中条目总数的能力-不仅仅是一个或全部。 the windows semaphore is exactly correspond to this. Windows信号量与此完全对应。 this is FIFO queue and by call ReleaseSemaphore we can exactly set amount of threads (entries) to wake - this is second parameter of api lReleaseCount . 这是FIFO队列,通过调用ReleaseSemaphore我们可以精确设置要唤醒的线程(条目)数量-这是api lReleaseCount的第二个参数。 in case event or ConditionVariable we can only wake single or all waiters. 万一事件或ConditionVariable,我们只能唤醒一个或所有服务员。

your mistake not in that you select semaphore - this is the best choice for this task. 您的错误不是因为您选择了信号量-这是完成此任务的最佳选择。 you mistake that you select it for wrong essences - sem_bridgecount, sem_bridgempty - which is not queue by sence at all. 您会误认为您选择了错误的本质-sem_bridgecount,sem_bridgempty-这根本不是按感觉排队的。 you ned have 2 semaphores for 2 directions - HANDLE _hSemaphore[2]; 您已向2个方向发送了2个信号灯HANDLE _hSemaphore[2]; - one semaphore per direction - create it as _hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) - initial count is 0 (!) and maximum count is unlimited (but can select any value >= 5). -每个方向一个信号量-创建为_hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) -初始计数为0(!),最大计数为无限(但可以选择_hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) 5的任何值)。 when car try enter to bridge in direction and can not, because now another direction is active or no free space on bridge - it must wait on semaphore (in FIFO queue) _hSemaphore[direction] . 当汽车尝试进入方向而不能进入桥时,因为现在另一个方向处于活动状态或桥上没有可用空间-它必须等待信号量(在FIFO队列中) _hSemaphore[direction] and when car exit from bridge - he need check current situation on bridge and wake one or another direction on some exactly cars count ( n ) (not all or single) - so call ReleaseSemaphore(_hSemaphore[direction], n, 0); 当汽车从桥上驶出时-他需要检查桥上的当前状况并唤醒一些确切的汽车计数( n )上的一个或另一个方向 (不是全部或单个)-因此调用ReleaseSemaphore(_hSemaphore[direction], n, 0);

in general: 一般来说:

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

and

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

note that in every enter - car only once enter to CriticalSection and after wait on _hSemaphore[direction] it just enter to bridge without again enter to cs and check conditions. 请注意,在每个回车中,汽车只有一次进入CriticalSection,在等待_hSemaphore[direction]它只是进入桥接而没有再次进入cs并检查条件。 this is because we can calculate exactly cars count (not single or all) and direction in exit - and wake only cars which and must enter to bridge, this will be impossible if use events or conditional variables 这是因为我们可以准确计算exit轿厢数量(不是单个或全部)和方向-并仅唤醒必须进入桥梁的轿厢,如果使用事件或条件变量,这将是不可能的

despite solution with conditional variables and CS is possible, i think it not best because: thread after wait in SleepConditionVariableCS - again enter to cs which is absolute not need we need or wake only single car by WakeConditionVariable when really can multiple cars enter to bridge, or wake all by WakeAllConditionVariable but in this case several threads in concurrent again try enter to the same cs and only one will be winner, another will be wait here count of waiting threads can be more than maximum place on bridge (5 in your case) - and some threads will be need begin wait again in loop. 尽管有条件变量和CS的解决方案是可能的,但我认为这不是最好的,因为: SleepConditionVariableCS等待后线程-再次输入cs,这绝对是我们不需要的,或者当多个车真的可以进入桥接时,由WakeConditionVariable唤醒单个车,或通过WakeAllConditionVariable唤醒所有WakeAllConditionVariable但在这种情况下,并发中的多个线程再次尝试输入相同的cs,只有一个将获胜,另一个将在这里等待,等待线程的数量可能超过网桥的最大位置(在您的情况下为5) -并且一些线程将需要开始再次在循环中等待。 all this can be avoid if correct use semaphore 如果正确使用信号量,可以避免所有这些情况

full working implementation here 完整的工作实施在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM