5

如果我只同时从多个线程调用并在调用或枚举队列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());
});
4

4 回答 4

17

该文档还指出,这种类型的实例成员不是线程安全的(向下滚动到线程安全部分)。

该文档还指出:

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

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

开玩笑: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对多线程的支持。

于 2012-08-24T14:07:57.590 回答
4

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

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

除非在特殊情况下,否则不要依赖无证财产。

于 2012-08-24T14:12:29.953 回答
1

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

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

于 2012-08-24T14:10:44.170 回答
0

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

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

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

于 2012-08-24T14:09:12.393 回答