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

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

问题描述

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

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.

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

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