【问题标题】:Is Queue<T>.Enqueue(T) method thread-safe when it is used alone?单独使用 Queue<T>.Enqueue(T) 方法是否是线程安全的?
【发布时间】:2012-08-24 14:03:58
【问题描述】:

如果我只同时从多个线程调用 Enqueue(T) 并等待这些线程完成,然后再调用 Dequeue() 或枚举队列,它会是线程安全的吗?

var queue = new Queue<int>();
Action enqueue = () =>
{
    for (int i = 0; i < 100000; i++)
        queue.Enqueue(i);
};

var tasks = new[]
{
    new Task(enqueue),
    new Task(enqueue),
    new Task(enqueue)
};
foreach (var task in tasks)
    task.Start();

Task.Factory.ContinueWhenAll(tasks, t =>
{
    while (queue.Count > 0)
        Console.WriteLine(queue.Dequeue());
});

【问题讨论】:

  • 如果你想要一个线程安全队列,为什么不使用ConcurrentQueue
  • @nemesv:我只需要从多个线程调用 Enqueue 方法,如果我的问题中的示例是线程安全的,我不需要使用 ConcurrentQueue。
  • @Daniel:你能发布一个“如果你尝试这样做,你会得到这个例外”这样的答案,这样我可以接受吗?
  • @ŞafakGür:Adam 已经发布了一个答案,告诉您它不是线程安全的。你可以接受。
  • @Daniel:刚看到,谢谢。

标签: c# asynchronous thread-safety queue


【解决方案1】:

The documentation 还声明此类型的实例成员不是线程安全的(向下滚动到线程安全部分)。

文档还指出:

一个队列可以同时支持多个读取器,只要 集合未修改。

但是,这只是并发读取不会改变列表这一事实的副产品。它确实使类型“线程安全”。最好将线程安全视为为定义类型的 公共合约 的所有操作提供 true 支持(在这种情况下,更改列表时的线程安全也是如此) .

开玩笑:Enqueue 的实现不包括任何线程同步或锁定原语:

public void Enqueue(T item)
{
    if (this._size == this._array.Length)
    {
        int num = (int)((long)this._array.Length * 200L / 100L);
        if (num < this._array.Length + 4)
        {
            num = this._array.Length + 4;
        }
        this.SetCapacity(num);
    }
    this._array[this._tail] = item;
    this._tail = (this._tail + 1) % this._array.Length;
    this._size++;
    this._version++;
}

所以我选择“不”。有ConcurrentQueue 支持多线程。

【讨论】:

  • +1:当多个线程入队时会发生什么的实际示例可以在here 找到。请注意非确定性行为。
  • 最重要的因素是文档所说的,而不是 current 实现的内容。 impl 可以随时更改,包括在补丁级别之间。
  • @usr 在您发表评论之前,我实际上已经更新了我的答案以指向文档。
  • @AdamHouldsworth arg!看看萨法克刚才说的话。可能是他认为实现可以证明线程安全(当然它只能证明不安全)。也许我疯了,但我认为人们很容易被这个误导。无论如何,您的回答尽可能地澄清了这一点。
  • @ŞafakGür 是的,我知道混乱会蔓延到哪里。所以是为了解决这些编程问题:-)
【解决方案2】:

这很重要:如果文档没有说队列是线程安全的,那么它不是。 (对于 Queue,他们说它不是线程安全的)。

查看内部是一项弱测试:内部可能随时更改为非线程安全版本。

除非在特殊情况下,否则不要依赖未记录的属性。

【讨论】:

  • 并且只依赖于像微软这样大的机构的文档。根据我的经验,文档(虽然大部分不是来自 Microsoft)也是一个薄弱的测试。
【解决方案3】:

我认为这不是线程安全的,因为 Queue 不是线程安全的。您正在将同一个实例共享给不同的线程。

一个队列可以同时支持多个读取器,只要集合没有被修改。即便如此,通过集合进行枚举本质上不是线程安全的过程。为了保证枚举过程中的线程安全,可以在整个枚举过程中锁定集合。要允许集合被多个线程访问以进行读写,您必须实现自己的同步。 http://msdn.microsoft.com/en-us/library/7977ey2c.aspx

【讨论】:

    【解决方案4】:

    这种类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。不保证任何实例成员都是线程安全的。

    http://msdn.microsoft.com/en-us/library/7977ey2c.aspx

    您需要实现自己的同步(即使用 lock()),或者使用 ConcurrentQueue (System.Collections.Concurrent)。

    【讨论】:

    • 没有queue.IsSynchronized 属性。
    • 如果有的话,永远都是假的。
    • 道歉;那是私有财产。固定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多