【问题标题】:Bounded Queue scenario有界队列场景
【发布时间】:2010-01-31 15:22:24
【问题描述】:

我需要实现一个生产者/消费者有界队列,多个消费者针对一个生产者。

我有一个推送函数,可以将一个项目添加到队列中,然后检查 maxsize。如果我们达到它返回 false,在其他情况下返回 true。

在下面的代码中_vector是一个List,onSignal基本上是以异步的方式消费一个item。

您是否发现此代码存在问题?

public bool Push(T message)
{
    bool canEnqueue = true;

    lock (_vector)
    {
        _vector.Add(message);
        if (_vector.Count >= _maxSize)
        {
            canEnqueue = false;
        }
    }

    var onSignal = SignalEvent;
    if (onSignal != null)
    {
        onSignal();
    }

    return canEnqueue;
}

【问题讨论】:

    标签: c# multithreading queue retlang


    【解决方案1】:

    我知道你说单生产者,多消费者,但无论如何还是值得一提:如果你的队列几乎满了(比如 25 个插槽中的 24 个),那么如果两个线程同时Push,你会最终超出限制。如果将来某个时候您甚至有可能拥有多个生产者,您应该考虑将Push 设为阻塞调用,并让它等待一个“可用”AutoResetEvent,该信号在一个项目出列或在仍有可用槽位的情况下将项目排入队列后。

    我看到的唯一其他潜在问题是SignalEvent。你没有向我们展示它的实现。如果它被声明为public event SignalEventDelegate SignalEvent,那么你会没事的,因为编译器会自动添加一个SynchronizedAttribute。但是,如果SignalEvent 使用带有add/remove 语法的支持委托,那么您将需要为事件本身提供自己的锁定,否则消费者可能会与事件分离一点点太晚了,之后仍然收到几个信号。

    编辑:实际上,无论如何这是可能的;更重要的是,如果您在没有适当锁定的情况下使用了属性样式的添加/删除委托,那么当您尝试执行委托时,委托实际上可能处于无效状态。即使是同步事件,消费者也需要准备好在取消订阅后接收(和丢弃)通知。

    除此之外我没有发现任何问题 - 虽然这并不意味着没有任何问题,这只是意味着我没有注意到任何问题。

    【讨论】:

    • Aaronaught,感谢您的及时反馈,在可预见的未来没有多个生产者,但最大尺寸也是一个松散的建议......我不需要严格执行它。 SignalEvent 被声明为 > 内部事件 Action SignalEvent;我不熟悉 SynchronizedAtribute ...将检查它。我正在 retlang 之上构建它......它确实有助于为多线程应用程序提供通用框架
    • @lboregard:如果您不在委托上使用自定义 add/remove 方法,则无需担心 SynchronizedAttribute - 编译器会为您生成它们。只有当您覆盖您需要实现自定义锁定的默认行为时。这似乎在很大程度上没有记录,但它是众所周知的。
    【解决方案2】:

    我看到的最大问题是使用List<T> 来实现队列;这样做存在性能问题,因为删除第一项涉及复制所有数据。

    其他想法;即使您没有添加数据,您也会发出信号,并且使用事件本身可能会出现线程问题(存在一些边缘情况,即使您在null 测试之前捕获值 - 加上它可能比使用Monitor 发出信号的开销更大)。

    我会切换到不会有这个问题的Queue<T> - 或者更好地使用预滚动示例;例如Creating a blocking Queue in .NET?,它完全符合您的讨论,并支持任意数量的生产者和消费者。它使用阻塞方法,但“尝试”方法是:

    public bool TryEnqueue(T item)
    {
        lock (queue)
        {
            if (queue.Count >= maxSize) { return false; }
            queue.Enqueue(item);
            if (queue.Count == 1)
            {
                // wake up any blocked dequeue
                Monitor.PulseAll(queue);
            }
            return true;
        }
    }
    

    最后 - 你不是“推”到一个堆栈,而不是一个队列吗?

    【讨论】:

    • 马克,感谢您的回复。我没有提到我需要能够在“队列”的顶部添加元素,所以直接的 Queue 对我没有帮助。这应该不经常发生,但我会很感激任何可以帮助性能的建议。我知道基于监视器的阻塞队列实现,但我使用 retlang 作为基础框架来管理线程之间的同步,我仍在学习它的复杂性,但我将重新考虑使用这样的阻塞队列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 1970-01-01
    相关资源
    最近更新 更多