簡體   English   中英

多個線程等待相同的信號量

[英]Multiple threads waiting for same 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號方向行駛。現在有3個方向2的輸入請求。所有3個將在WaitForSingleObject(sem_bridgempty, INFINITE);處阻塞WaitForSingleObject(sem_bridgempty, INFINITE); 現在,當橋空了時,三個橋架中的一個將被拾起。一個被拾起的橋將再次使橋不為空。然后,即使方向相同,另外兩個橋仍然將等待橋架變空。 因此,即使橋上有direction=2汽車,其他具有相同方向的汽車仍在等待sem_bridgempty 我甚至想用的sem_bridgempty作為一個事件,而不是semaphore (在SetEvent的() exit1()cars_count=0resetevent()enter()當第一輛車進入),但仍然是所有線程不會醒來。

最干凈的選擇是使用關鍵部分和條件變量。

ENTER算法如下所示:

  • 聲明關鍵部分。
  • 循環調用SleepConditionVariableCS,如使用條件變量中所示,直到:
    • 交通往正確的方向行駛,而橋梁的通行能力還剩下,或者
    • 橋是空的。
  • 更新狀態以代表您的汽車進入橋梁。
  • 釋放關鍵部分。

EXIT算法如下所示:

  • 聲明關鍵部分。
  • 更新狀態以表示您的汽車駛離橋梁。
  • 釋放關鍵部分。
  • 調用WakeConditionVariable。

條件變量可以是整數,其大小表示橋梁上的轎廂數量,其符號表示行進方向。


如果要避免使用條件變量,那么我能想到的最簡單的解決方案需要一個關鍵部分和三個自動重置事件:一個用於每個行駛方向,另一個用於指示橋是空的。 您還將需要一個變量,代表橋上的汽車數量。

ENTER算法如下所示:

  • 使用WaitForMultipleObjects,聲明與您的行進方向相對應的事件或與橋梁相對應的事件為空,以先到者為准。
  • 輸入關鍵部分。
  • 遞增計數以代表您的汽車駛入橋梁。
  • 如果計數未達到極限,則設置代表您的行進方向的事件。
  • 離開關鍵部分。

EXIT算法如下所示:

  • 輸入關鍵部分。
  • 減少計數以代表您的汽車駛離橋梁。
  • 如果計數為零,則設置指示橋為空的事件。
  • 如果計數不為零,則根據您的行進方向設置事件。
  • 釋放關鍵部分。

需要創建最符合任務的對象。 在當前任務中-我們有2個隊列-雙向。 這兩個隊列在意義上都是FIFO。 並且我們需要具有喚醒隊列中條目總數的能力-不僅僅是一個或全部。 Windows信號量與此完全對應。 這是FIFO隊列,通過調用ReleaseSemaphore我們可以精確設置要喚醒的線程(條目)數量-這是api lReleaseCount的第二個參數。 萬一事件或ConditionVariable,我們只能喚醒一個或所有服務員。

您的錯誤不是因為您選擇了信號量-這是完成此任務的最佳選擇。 您會誤認為您選擇了錯誤的本質-sem_bridgecount,sem_bridgempty-這根本不是按感覺排隊的。 您已向2個方向發送了2個信號燈HANDLE _hSemaphore[2]; -每個方向一個信號量-創建為_hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) -初始計數為0(!),最大計數為無限(但可以選擇_hSemaphore[0] = CreateSemaphore(0, 0, MAXLONG, 0) 5的任何值)。 當汽車嘗試進入方向而不能進入橋時,因為現在另一個方向處於活動狀態或橋上沒有可用空間-它必須等待信號量(在FIFO隊列中) _hSemaphore[direction] 當汽車從橋上駛出時-他需要檢查橋上的當前狀況並喚醒一些確切的汽車計數( n )上的一個或另一個方向 (不是全部或單個)-因此調用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喚醒所有WakeAllConditionVariable但在這種情況下,並發中的多個線程再次嘗試輸入相同的cs,只有一個將獲勝,另一個將在這里等待,等待線程的數量可能超過網橋的最大位置(在您的情況下為5) -並且一些線程將需要開始再次在循環中等待。 如果正確使用信號量,可以避免所有這些情況

完整的工作實施在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM