2017-07-16 214 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