Thread safe queue - Enqueue / Dequeue(线程安全队列 - 入队/出队)
问题描述
首先,我将解释一个简短的场景;
Firstly, i'll explain a short scenario;
作为来自某些设备的信号触发,警报类型的对象被添加到队列中.每隔一段时间,就会检查队列,并且对于队列中的每个警报,它都会触发一个方法.
As a signal from certain devices triggers, an object of type Alarm is added to a queue. At an interval, the queue is checked, and for each Alarm in the queue, it fires a method.
但是,我遇到的问题是,如果在遍历队列时将警报添加到队列中,则会引发错误,说明队列在您使用时已更改.这里有一些代码来显示我的队列,假设警报不断插入其中;
However, the problem i'm running into is that, if an alarm is added to the queue whilst it's being traversed, it throws an error to say that the queue has changed whilst you were using it. Here's a bit of code to show my queue, just assume that alarms are being constantly inserted into it;
public class AlarmQueueManager
{
public ConcurrentQueue<Alarm> alarmQueue = new ConcurrentQueue<Alarm>();
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
{
}
}
所以我的问题是,我如何使这个更...线程安全?这样我就不会遇到这些问题.也许类似于将队列复制到另一个队列,处理那个队列,然后将原始队列中处理的警报出队?
So my question is, how do i make this more...thread safe? So that i won't run into these issues. Perhaps something along the lines of, copying the queue to another queue, working on that one, then dequeueing the alarms that were dealt with from the original queue?
刚刚被告知并发队列,现在检查一下
edit: just been informed of concurrent queue, will check this out now
推荐答案
private void DeQueueAlarm()
{
Alarm alarm;
while (alarmQueue.TryDequeue(out alarm))
SendAlarm(alarm);
}
或者,您可以使用:
private void DeQueueAlarm()
{
foreach (Alarm alarm in alarmQueue)
SendAlarm(alarm);
}
根据ConcurrentQueue<T>.GetEnumerator
:
Per the MSDN article on ConcurrentQueue<T>.GetEnumerator
:
枚举表示队列内容的即时快照.在调用 GetEnumerator
后,它不会反映对集合的任何更新.枚举器可以安全地与队列的读取和写入同时使用.
The enumeration represents a moment-in-time snapshot of the contents of the queue. It does not reflect any updates to the collection after
GetEnumerator
was called. The enumerator is safe to use concurrently with reads from and writes to the queue.
因此,当您的 DeQueueAlarm
方法被多个线程同时调用时,两种方法之间的差异就会出现.使用 TryQueue
方法,可以保证队列中的每个 Alarm
只会被处理一次;然而,哪个线程选择哪个警报是不确定的.foreach
方法确保每个竞赛线程将处理队列中的所有警报(从它开始迭代它们的时间点开始),从而导致多次处理相同的警报.
Thus, the difference between the two approaches arises when your DeQueueAlarm
method is called concurrently by multiple threads. Using the TryQueue
approach, you are guaranteed that each Alarm
in the queue would only get processed once; however, which thread picks which alarm is determined non-deterministically. The foreach
approach ensures that each racing thread will process all alarms in the queue (as of the point in time when it started iterating over them), resulting in the same alarm being processed multiple times.
如果您想只处理每个警报一次,然后将其从队列中删除,您应该使用第一种方法.
If you want to process each alarm exactly once, and subsequently remove it from the queue, you should use the first approach.
这篇关于线程安全队列 - 入队/出队的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:线程安全队列 - 入队/出队
基础教程推荐
- SSE 浮点算术是否可重现? 2022-01-01
- MS Visual Studio .NET 的替代品 2022-01-01
- rabbitmq 的 REST API 2022-01-01
- 将 XML 转换为通用列表 2022-01-01
- 如何在 IDE 中获取 Xamarin Studio C# 输出? 2022-01-01
- c# Math.Sqrt 实现 2022-01-01
- 将 Office 安装到 Windows 容器 (servercore:ltsc2019) 失败,错误代码为 17002 2022-01-01
- 如何激活MC67中的红灯 2022-01-01
- 有没有办法忽略 2GB 文件上传的 maxRequestLength 限制? 2022-01-01
- 为什么Flurl.Http DownloadFileAsync/Http客户端GetAsync需要 2022-09-30