并发下的队列为不同类型提供不同的结果
我刚刚开始了解一些新的.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
类。