2011-12-06 45 views
1

首先,我知道它可以用互斥量和條件變量實現,但我希望實現最有效的實現。 當沒有爭用時,我想要一個快速路徑的信號量。在Linux上,使用futex很容易;例如,這裏有一個等待:Windows上的快速計數信號量?

if (AtomicDecremenIfPositive(_counter) > 0) return; // Uncontended 
AtomicAdd(&_waiters, 1); 
do 
{ 
    if (syscall(SYS_futex, &_counter, FUTEX_WAIT_PRIVATE, 0, nullptr, nullptr, 0) == -1) // Sleep 
    { 
     AtomicAdd(&_waiters, -1); 
     throw std::runtime_error("Failed to wait for futex"); 
    } 
} 
while (AtomicDecrementIfPositive(_counter) <= 0); 
AtomicAdd(&_waiters, -1); 

和崗位:

AtomicAdd(&_counter, 1); 
if (Load(_waiters) > 0 && syscall(SYS_futex, &_counter, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0) == -1) throw std::runtime_error("Failed to wake futex"); // Wake one 

起初我以爲的Windows只使用NtWaitForKeyedEvent()。問題在於它不是直接替換,因爲它在進入內核之前不會自動檢查_counter處的值,所以可能會錯過NtReleaseKeyedEvent()的喚醒。更糟的是,NtReleaseKeyedEvent()會阻塞。 什麼是最好的解決方案?

+0

信號量限制併發訪問共享資源的數量。互斥體將訪問序列化,以便併發用戶必須等待。你要哪個? – AJG85

+1

信號量。互斥鎖應該被鎖定它們的線程解鎖。我需要線程等待其他人發佈的信號量。 –

+0

嗯..第一個想法:如果信號計數由一個整數表示,sema上的原子減量導致負數表示調用者需要等待。導致零或負結果的sema上的原子增量表示需要釋放一個等待線程。這是否會與你的'超級CS'之一一起保護線程等待的事件列表,在某些情況下爲更快的「無內核」路徑創建更好的信號量? –

回答

2

我覺得這樣的事情應該工作:

// bottom 16 bits: post count 
// top 16 bits: wait count 
struct Semaphore { unsigned val; } 

wait(struct Semaphore *s) 
{ 
retry: 
    do 
     old = s->val; 
     if old had posts (bottom 16 bits != 0) 
      new = old - 1 
      wait = false 
     else 
      new = old + 65536 
      wait = true 
    until successful CAS of &s->val from old to new 

    if wait == true 
     wait on keyed event 
     goto retry; 
} 

post(struct Semaphore *s) 
{ 
    do 
     old = s->val; 
     if old had waiters (top 16 bits != 0) 
      // perhaps new = old - 65536 and remove the "goto retry" above? 
      // not sure, but this is safer... 
      new = old - 65536 + 1 
      release = true 
     else 
      new = old + 1 
      release = false 
    until successful CAS of &s->val from old to new 

    if release == true 
     release keyed event 
} 

編輯:這麼說,我不知道這會幫助你很多。您的線程池通常應該足夠大,以便線程隨時可以處理您的請求。這意味着不僅等待,而且帖子總是會走慢路徑並進入內核。因此,計算信號量可能是您不太關心僅用戶空間的快速路徑的一個原始信號。股票Win32信號量應該足夠好。這就是說,我很高興被證明是錯誤的!

3

Windows具有本機信號量CreateSemaphore。除非以正常方式進行,否則,除非您有某種形式的文檔性能問題,否則您甚至不應考慮易碎或硬件特定的優化。

+1

我使用自定義方式替代Windows臨界區(而且速度比slim rw鎖快一些),從而獲得了一個數量級的性能提升,因此對於相同的重要改進有非常合理的預期信號燈。即使在無爭議的情況下,本地信號也是重要的內核調用,這很常見。不必要的內核調用比檢查是否需要的原子操作慢10倍。 –

+0

如果你有太多的調用同步函數的開銷不會在噪聲中丟失,那麼你要麼寫*非常低級的代碼,要麼你完全不恰當地使用同步函數。 –

+0

這是後者。我對x86和x86-64特有的問題沒有任何問題,因爲我控制了部署機器的規範。無論如何,我想回到我原來的問題。 –

0

Qt擁有各種各樣的東西,如QMutex,QSemaphore,它們在您的問題中表達出來的精神實現。

其實,我會建議用通常的OS提供的同步原語來替換futex的東西;無論如何,這應該無關緊要,因爲這是緩慢的道路。

+0

我應該補充一點,Qt還提供了構建自己的同步機制所需的必要的原子操作(比較和交換最顯着)。 – Ringding

1

我投你的第一個想法,例如臨界區和條件變量。關鍵部分速度夠快,它在進入睡眠之前使用互鎖操作。或者,您可以嘗試使用SRWLocks而不是關鍵部分。條件變量(和SRWLocks)速度非常快 - 它們唯一的問題是XP上沒有條件,但也許你不需要定位這個平臺。