并发下的队列<t>为不同类型提供不同的结果</t>

时间:2012-08-16 06:16:57

标签: c# .net collections concurrency

我刚刚开始了解一些新的.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已经入队。

为什么没有例外?

所有未列出的项目发生了什么变化?

这与Queue

的功能相同
    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 个答案:

答案 0 :(得分:4)

根据MSDN,

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

答案 1 :(得分: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不是线程安全的。这里发生的事情称为"Race condition"

答案 2 :(得分:2)

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

至于&#34;失去&#34;项目,它无法确定 - 由于多线程访问,队列的内部状态可能已损坏,因此计数本身可能是错误的,或者项目可能根本就没有排队。< / p>

答案 3 :(得分:1)

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

因此,请考虑使用ConcurrentQueue<T>类。