【问题标题】:Thread safe queue - Enqueue / Dequeue线程安全队列 - 入队/出队
【发布时间】:2012-11-05 04:42:22
【问题描述】:

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

作为来自某些设备的信号触发,警报类型的对象被添加到队列中。每隔一段时间,就会检查队列,并且对于队列中的每个警报,它都会触发一个方法。

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

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
        {
        }
    }

所以我的问题是,我如何使这个更...线程安全?这样我就不会遇到这些问题。也许类似于将队列复制到另一个队列,处理那个队列,然后将原始队列中处理的警报出队?

编辑:刚刚被告知并发队列,现在检查一下

【问题讨论】:

  • 您应该首先从队列中弹出项目,然后是 SendAlarm,以及使用通用线程安全队列实现。如果您无法处理某个项目,请再次将其排入队列。

标签: c# multithreading queue


【解决方案1】:

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

【讨论】:

  • 我在使用“.TryDequeue()”时遇到了一些问题。你能帮我吗? :) 我会将我的问题更新为 ConcurrentQueue
【解决方案2】:

任何你不能使用ConcurrentQueue&lt;T&gt;的原因

【讨论】:

  • 啊,我不知道这个!会去看看
【解决方案3】:
private void DeQueueAlarm()
{
    Alarm alarm;
    while (alarmQueue.TryDequeue(out alarm))
        SendAlarm(alarm);
}

或者,您可以使用:

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

根据ConcurrentQueue&lt;T&gt;.GetEnumerator 上的 MSDN 文章:

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

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

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

【讨论】:

  • 所以仅供参考,TryDequeue 推出一个警报,我们将其填充到之前声明的警报变量中,然后使用它?
  • 没错。此外,TryDequeue 在列表为空时返回false,导致我们跳出while 循环。
  • 这很漂亮:) 现在要测试这个。
  • 第一种方法很有魅力,正是我想要的。猜猜这是其中之一,一旦你知道了,你就知道了!谢谢
【解决方案4】:

考虑到每个线程实际上一次只处理一个警报,一个更好的方法是替换它:

        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);
        }

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 2018-08-15
    • 2014-03-13
    相关资源
    最近更新 更多