1

我刚刚开始了解一些新的 .NET 并发集合,例如 ConcurrentDictionary 和 ConcurrentQueue,并且我正在运行一些测试以查看当我并行写入队列时会发生什么。

所以我运行了这个:

    private static void ParallelWriteToQueue(Queue<int> queue)
    {
        Stopwatch sw = Stopwatch.StartNew();
        Parallel.For(1,1000001,(i) => queue.Enqueue(i));
        sw.Stop();
        Console.WriteLine("Regular int Queue - " + queue.Count + " time" + sw.ElapsedMilliseconds);
    }

正如我认为我得到了下一个例外:

Source array was not long enough. Check srcIndex and length, and the array's lower bounds.

所以这个队列不能像预测的那样处理并发的入队。

但是,当我将队列的类型更改为字符串时,没有异常,结果写成类似

Regular string Queue - 663209 time117

这意味着只有大约 663k 被排队。

为什么没有例外?

所有未排队的项目发生了什么?

这是与队列相同的功能

    private static void ParallelWriteToQueue(Queue<string> queue)
    {
        Stopwatch sw = Stopwatch.StartNew();
        Parallel.For(1, 100001, (i) => queue.Enqueue(i.ToString()));
        sw.Stop();
        Console.WriteLine("Regular string Queue - " + queue.Count + " time" + +sw.ElapsedMilliseconds);
    }
4

4 回答 4

4

Queue<T>ConcurrentQueue<T>不是线程安全的,根据 MSDN。您描述的其余行为是由并发(多线程)写访问引起的冲突偶然发生的,纯粹基于Queue<T>不是线程安全的事实。

于 2012-08-16T06:25:00.023 回答
2

你是否得到异常与你放入队列的类型无关。它是非确定性的,我可以重现这两种类型的异常,并且我也可以无异常地重现这两种类型的情况——无需更改代码。

运行以下代码段显示了这一点:

int exceptions = 0;
int noExceptions = 0;
for (int x = 0; x < 100; ++x)
{
    Queue<int> q = new Queue<int>();
    try
    {
        Parallel.For(1,1000001,(i) => q.Enqueue(i)); 
        ++noExceptions;
    }
    catch
    {
        ++exceptions;
    }
}

Console.WriteLine("Runs with exception: {0}. Runs without: {1}", exceptions, noExceptions);

输出类似于Runs with exception: 96. Runs without: 4

原因是 - 正如其他人已经提到的 - 这Queue不是线程安全的。这里发生的事情称为“比赛条件”

于 2012-08-16T06:28:57.370 回答
2

您的测试表明,标准集合实现不是线程安全的。使用整数而不是字符串引发异常的事实可能只是偶然,如果再次尝试测试,您可能会得到不同的结果。

至于“丢失”的项目,无法确定 - 队列的内部状态可能由于多线程访问而损坏,因此计数本身可能是错误的,或者项目可能根本没有入队。

于 2012-08-16T06:30:24.420 回答
1

由于您使用Parallel.For()的是 ,因此集合必须是线程安全的才能提供正常工作。

所以,考虑使用ConcurrentQueue<T>类。

于 2012-08-16T06:24:27.220 回答