线程安全队列-入队/出队 [英] Thread safe queue - Enqueue / Dequeue

查看:135
本文介绍了线程安全队列-入队/出队的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Firstly, i'll explain a short scenario;

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

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 上的MSDN文章:

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.

这篇关于线程安全队列-入队/出队的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆