我刚刚开始了解一些新的.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);
}
答案 0 :(得分:4)
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>
类。