2010-07-21 36 views
1

我使用ConcurrentQueue在2個線程之間可以嗎?我想檢查我不需要明確地「鎖定」任何地方。特別是看看在那裏我有意見,我會放棄此數據包中的行...我在2線程之間使用ConcurrentQueue可以嗎?

public class PacketCapturer 
{ 

private static ConcurrentQueue<Packet> _packetQueue = new ConcurrentQueue<Packet>(); 

public PacketCapturer(IPHostEntry proxyDns, ref BackgroundWorker bw) 
{ 
    // start the background thread 
    var backgroundThread = new System.Threading.Thread(BackgroundThread); 
    backgroundThread.Start(); 

    // Start Packet Capture with callback via PacketCapturerCallback 
} 

private void PacketCapturerCallback(Packet packet) 
{ 
    _packetQueue.Enqueue(packet); 
} 

private static void BackgroundThread() 
{ 
    while (!BackgroundThreadStop) 
    { 
     if (_packetQueue.Count == 0) Thread.Sleep(250); 
     else 
     { 
      ConcurrentQueue<Packet> ourQueue; 
      ourQueue = _packetQueue; // COULD I DROP A PACKET BETWEEN HERE 
      _packetQueue = new ConcurrentQueue<Packet>();  // AND HERE??? 

      Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count); 
     } 
    } 
} 
+0

你永遠不要使用隊列同時(也就是,你永遠不會從第二個線程訪問它),那麼,爲什麼你甚至使用ConcurrentQueue? – Gabe 2010-07-21 05:19:59

回答

1

編輯:我認爲馬克/加布是正確的,你不會丟失任何數據包。如果其他人能夠衡量這一點,我將留下其餘部分以供參考。


簡單地說,是的。您可能會在此丟失一個或多個數據包。您可能需要查看ConcurrentQueue提供的方法來獲取/刪除它的一部分,因爲它看起來像你想做的事情。

爲什麼不TryDequeue,直到它返回false:

else 
    { 
     Queue<Packet> ourQueue = new Queue<Packet>(); //this doesn't need to be concurrent unless you want it to be for some other reason. 
     Packet p; 
     while(_packetQueue.TryDequeue(out p)) 
     { 
      ourQueue.Enqueue(p); 
     } 

     Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count); 
    } 
+0

@Chad - 謹慎解釋* how *「yes」? – 2010-07-21 05:11:41

+0

Marc ...我的意思是說,他可能會丟失一個或多個數據包...這裏是如何: 線程1:ourQueue = _packetQueue; 線程2:_packetQueue.Enqueue(packet); //此數據包丟失 ...更多排隊可能發生在這裏 線程1:_packetQueue = new ConcurrentQueue (); – Chad 2010-07-21 05:17:11

+0

我可以同時向兩(2)個線程添加和刪除隊列的概念對我來說似乎很好 - Queue是否支持此併發使用?它應該是ConcurrentQueue嗎? – Greg 2010-07-21 05:17:41

2

沒有,也不行。首先,如果你在並行線程改變這樣的引用,_packetQueue 必須標明揮發,防止編譯器和代碼生成優化從從來沒有看到變化。 _packetQueue shoudl的調用通常以Interlocked.CompareExchange的形式出現,但這對於您的使用來說並不那麼重要。

但是對於更worying是在後臺線程改變packetQueue實例這樣的模式。這樣做的目的是什麼?它有一個horible代碼味道...

更新

我ususaly做的是這樣的:

生產線(S):

Producer() { 
... 
lock(_sharedQueue) { 
    _sharedQueue.Enqueue(something); 
} 
... 
} 

消費者線程:

consumer (...) { 
... 
var Something[] toProcess = null; 
lock(_sharedQueue) 
{ 
    toProcess = _sharedQueue.Toarray(); 
    _sharedQueue.Clear(); 
} 
// Process here the toProcess array 
... 
} 

這是每一個SINGL不夠好我使用過的。處理不會在鎖下發生,因此鎖定很小。 ConcurrentQueue沒有必要,一個普通的.Net 2.0集合就足夠了。通常我會使用專用的鎖定對象,以便實踐,而不是鎖定實際的隊列實例。

+0

謝謝 - 我喜歡乍得的建議,如果工作正常 – Greg 2010-07-21 05:19:08

+0

你的目標是什麼?你是否在爭取一個免鎖隊列?複製出來的元素不會被鎖定,ConcurrentQueue *會*在內部使用鎖定。實現無鎖隊列無論如何都不是微不足道的,我非常懷疑你真的需要它。具有+64內核的系統需要無鎖結構,並且像Os調度程序那樣執行極其緊張和時間關鍵的工作。 – 2010-07-21 05:26:08

+0

我剛剛解除了數據包捕獲回調的真正方法,因此要保持回調本身非常短,並且使用另一個線程 – Greg 2010-07-21 05:38:30

1

我不認爲你可以下降包,因爲顯然任何線程是要取消引用到要麼舊的或新的。邊緣的情況是,你得到後ourQueue更多的數據來了,你認爲你已經換他們,或者你也可能最終沒有注意到在Sleep環的參考變化(但:2個線程,唯一的線程更改參考這個線程,所以不一定是個問題)。

TBH,如果你有2個線程在這裏,爲什麼不lock或使用ReaderWriterLock?這將是一個簡單得多弄清楚的時候,會發生什麼......

+0

謝謝 - 我喜歡乍得的建議,如果這工作正常 – Greg 2010-07-21 05:19:33

+0

馬克 - 你有什麼評論重新使用鎖或ReadWriterLock,或去與乍得給的例子? – Greg 2010-07-21 05:24:42

+0

@Greg - 如果您沒有*交換隊列(出於某種原因),那麼這是理想的。隨着乍得的更新,這看起來很合理。 – 2010-07-21 06:06:05

相關問題