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.
这篇关于线程安全队列 - 入队/出队的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:线程安全队列 - 入队/出队
- MoreLinq maxBy vs LINQ max + where 2022-01-01
- C# 中多线程网络服务器的模式 2022-01-01
- Web Api 中的 Swagger .netcore 3.1,使用 swagger UI 设置日期时间格式 2022-01-01
- C#MongoDB使用Builders查找派生对象 2022-09-04
- WebMatrix WebSecurity PasswordSalt 2022-01-01
- 如何用自己压缩一个 IEnumerable 2022-01-01
- 良好实践:如何重用 .csproj 和 .sln 文件来为 CI 创建 2022-01-01
- 带有服务/守护程序应用程序的 Microsoft Graph CSharp SDK 和 OneDrive for Business - 配额方面返回 null 2022-01-01
- 在哪里可以找到使用中的C#/XML文档注释的好例子? 2022-01-01
- 输入按键事件处理程序 2022-01-01