【问题标题】:Thread safe limited size queue线程安全的有限大小队列
【发布时间】:2015-09-07 09:29:22
【问题描述】:

我正在尝试编写一个 subj 队列,但我遇到了死锁和其他多线程问题。我想使用Interlocked.CompareExchange 来避免使用lock。但是这段代码没有按预期工作:它只是擦除整个队列。我在这里做错了什么?

public class FixedSizedQueue<T> : IEnumerable<T>
{
    readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
    public int Limit { get; set; }

    public FixedSizedQueue(int limit)
    {
        Limit = limit;
    }

    public void Enqueue(T obj)
    {
        _queue.Enqueue(obj);
        if (_queue.Count <= Limit)
            return;
        int count = _queue.Count;
        if (_queue.Count != Interlocked.CompareExchange(ref count, count, _queue.Count))
        {
            T overflow;
            while (_queue.TryDequeue(out overflow))
            {

            }
        }
    }

    public T[] ToArray()
    {
        return _queue.ToArray();
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _queue.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

也许我只需要另一个线程来切断队列......

【问题讨论】:

  • 好吧,您的 while 循环清楚地擦除了整个队列。那是行不通的。限制那个循环。
  • 引用Eric Lippert:“就拿锁吧。”
  • @Corak tnx 链接,我遵循这个建议
  • 使用 BlockingCollection 代替,BoundedCapacity 属性设置限制。它在底层使用 ConcurrentQueue 来实现集合。

标签: c# .net multithreading synchronization queue


【解决方案1】:

Interlocked.CompareExchange 在堆栈变量count 上毫无意义,因为它是从单线程访问的。正如我猜想的那样,您尝试在_queue.Count 上使用此方法,但由于.Count 是一个属性,而不是一个简单的变量,因此编译失败。所以你需要在你的类中定义计数器。

public class FixedSizedQueue<T> : IEnumerable<T>
{
    readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
    int CountShadow = 0; // Counter for check constraints.
    public int Limit { get; set; }

    public FixedSizedQueue(int limit)
    {
        Limit = limit;
    }

    public void Enqueue(T obj)
    {
        /* Update shadow counter first for check constraints. */
        int count = CountShadow;
        while(true)
        {
             if(count => Limit) return; // Adding element would violate constraint
             int countOld = Interlocked.CompareExchange(ref CountShadow, count, count + 1);
             if(countOld == count) break; //Successful update
             count = countOld;
        }
        _queue.Enqueue(obj); // This will update real counter.
    }
    ...
}

另外,您需要为Limit 属性设置自己的设置器,这将保持不变的CountShadow &lt;= Limit。或者只是禁止用户在对象构造后设置该属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2012-06-18
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2013-02-23
    • 1970-01-01
    相关资源
    最近更新 更多