线程安全队列 – 入队/出队

首先,我将解释一个简短的场景;

当来自某些设备的信号触发时,会将类型为Alarm的对象添加到队列中。 每隔一段时间检查一次队列,对于队列中的每个Alarm,它会触发一个方法。

但是,我遇到的问题是,如果在遍历队列时将警报添加到队列中,则会在您使用它时发出错误说队列已更改。 这里有一些代码来显示我的队列,只是假设警报不断被插入其中;

public class AlarmQueueManager { public ConcurrentQueue alarmQueue = new ConcurrentQueue(); System.Timers.Timer timer; public AlarmQueueManager() { timer = new System.Timers.Timer(1000); timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); timer.Enabled = true; } void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { DeQueueAlarm(); } private void DeQueueAlarm() { try { foreach (Alarm alarm in alarmQueue) { SendAlarm(alarm); alarmQueue.TryDequeue(); //having some trouble here with TryDequeue.. } } catch { } } 

所以我的问题是,我如何让这更多……线程安全? 所以我不会遇到这些问题。 也许是这样的事情,将队列复制到另一个队列,处理那个队列,然后将从原始队列处理的警报出列?

编辑:刚刚被告知并发队列,现在将检查出来

 private void DeQueueAlarm() { Alarm alarm; while (alarmQueue.TryDequeue(out alarm)) SendAlarm(alarm); } 

或者,您可以使用:

 private void DeQueueAlarm() { foreach (Alarm alarm in alarmQueue) SendAlarm(alarm); } 

根据ConcurrentQueue.GetEnumerator上的MSDN文章:

枚举表示队列内容的时刻快照。 调用GetEnumerator后,它不会反映对集合的任何更新。 枚举器可以安全地与队列的读取和写入同时使用。

因此,当多个线程同时调用DeQueueAlarm方法时,会出现两种方法之间的差异。 使用TryQueue方法,可以保证队列中的每个Alarm只会被处理一次; 但是,哪个线程选择哪个警报是非确定性的。 foreach方法确保每个赛车线程将处理队列中的所有警报(从它开始迭代它们的时间点开始),导致多次处理相同的警报。

如果您只想处理每个警报一次,然后将其从队列中删除,则应使用第一种方法。

任何原因你不能使用ConcurrentQueue

.Net已经有一个线程安全的队列实现:看看ConcurrentQueue 。

一个更好的方法来处理它,假设每个线程实际上只是一次处理一个警报,将取代这个:

  foreach (Alarm alarm in alarmQueue) { SendAlarm(alarm); alarmQueue.TryDequeue(); //having some trouble here with TryDequeue.. } 

有了这个:

  while (!alarmQueue.IsEmpty) { Alarm alarm; if (!alarmQueue.TryDequeue(out alarm)) continue; SendAlarm(alarm); } 

根本没有理由在任何时候获得队列的完整快照,因为您只关心在每个周期开始时要处理的下一个队列。