并发下的队列为不同类型提供不同的结果

我刚刚开始了解一些新的.NET并发集合,如ConcurrentDictionary和ConcurrentQueue,我正在运行一些测试,看看当我与Queue并行编写时会发生什么。

所以我跑了这个:

private static void ParallelWriteToQueue(Queue 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. 

因此,此队列无法像预测的那样处理并发队列。

但是 ,当我将队列的类型更改为字符串时,没有exception,结果写了类似的东西

 Regular string Queue - 663209 time117 

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

为什么没有例外?

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

这与Queue的function相同

  private static void ParallelWriteToQueue(Queue 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); } 

是否获得exception与您放入队列的类型无关。 它是非确定性的,我可以为这两种类型重现exception,我也可以毫无例外地重现这两种类型的情况 – 无需更改代码。

运行以下代码段显示:

 int exceptions = 0; int noExceptions = 0; for (int x = 0; x < 100; ++x) { Queue q = new Queue(); 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不是线程安全的。 这里发生的事情被称为“竞争条件” 。

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

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

至于“丢失”的项目,无法确定 – 由于multithreading访问,队列的内部状态可能已损坏,因此计数本身可能是错误的,或者项目可能根本就没有排队。

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

因此,请考虑使用ConcurrentQueue类。