2016-09-27 56 views
1

我得到了一個歸結爲生產者 - 消費者模式的應用程序。有幾個線程正在做一些工作並更新單個數據集,以便多個線程可以使用這些數據並自行完成工作。目前,這並不是非常複雜,所有消費線程都會等待數據集,直到其中一個生產者調用一個pulseall。等待多個不同的脈衝事件

現在有一個願望是在任何一個集合發生變化時,讓其中一個消費者線程從兩個不同的數據集中消費。團隊希望將重構保持在最低限度,並且我對線程的有限經驗給我一些問題找到了一個乾淨的解決方案。

快速和骯髒的解決方案是做一個單獨的對象的等待和脈動,並讓消費者線程檢查其數據集中的變化,然後再繼續。似乎沒有辦法讓一個線程等待兩個對象,而不用一個更強大的線程工具(線程池,任務等)替換通用線程,除非我沒有正確的谷歌。

+1

你看過['WaitHandle.WaitAny'](https://msdn.microsoft.com/en-us/library/system.threading.waithandle.waitany(v = vs.110).aspx)嗎?雖然,如果您正在使用'Monitor'而不是像'ManualResetEvent'那樣的東西,那麼您可能無法使用它。 –

+1

看到與一個線程等待兩個對象相關的代碼會很有幫助。 –

+0

@ScottChamberlain目前使用'Monitor.PulseAll'來喚醒所有消費者線程。我關於'ManualResetEvent'的問題是,一個事件可以用於多個消費者線程還是應該是1個事件1線程?如果1個事件可以用於所有線程,你如何處理來自多線程的重置? @devlincarnate它實際上並不等待兩個對象,它等待一個對象,然後檢查是否需要爲任何數據集執行任何操作。 – phill

回答

2

如果你願意做一點重構,我建議從Monitor切換到EventWaitHandle派生類之一。

取決於你想你可能想AutoResetEvent的行爲,將更加緊密地像一個Monitor.Entier(obj)/Monitor.Exit(obj)

private readonly object _lockobj = new Object(); 
public void LockResource() 
{ 
    Monitor.Enter(_lockobj); 
} 

public void FreeResource() 
{ 
    Monitor.Exit(_lockobj); 
} 

//Which is the same as 

private readonly AutoResetEvent _lockobj = new AutoResetEvent(true); 
public void LockResource() 
{ 
    _lockobj.WaitOne(); 
} 

public void FreeResource() 
{ 
    _lockobj.Set(); 
} 

,或者你想ManualResetEvent會喜歡更緊密地行事Monitor.Wait(obj)/Monitor.PulseAll(obj)

private readonly object _lockobj = new Object(); 
public void LockResource() 
{ 
    Monitor.Enter(_lockobj); 
} 

public bool WaitForResource() 
{ 
    //requires to be inside of a lock. 
    //returns true if it is the lock holder. 
    return Monitor.Wait(_lockobj);   
} 

public void SignalAll() 
{ 
    Monitor.PulseAll(_lockobj); 
} 

// Is very close to 

private readonly ManualResetEvent _lockobj = new ManualResetEvent(true); 
public bool LockResource() 
{ 
    //Returns true if it was able to perform the lock. 
    return _lockobj.Reset(); 
} 

public void WaitForResource() 
{ 
    //Does not require to be in a lock. 
    //if the _lockobj is in the signaled state this call does not block. 
    _lockobj.WaitOne();  
} 

public void SignalAll() 
{ 
    _lockobj.Set(); 
} 

1個事件可以喚醒多個線程,可以通過一個線程處理多個事件

ManualResetEvent resetEvent0 = ... 
ManualResetEvent resetEvent1 = ... 

public int WaitForEvent() 
{ 
    int i = WaitHandle.WaitAny(new WaitHandle[] {resetEvent0, resetEvent1}); 
    return i; 
} 

i將是已調用Set()的重置事件的索引。