ConcurrentQueue c#,不精确的结果?
问题描述:
我想问一下关于c#队列的问题# 如果ConcurrentQueue是安全线程,为什么这个代码的结果是〜98k? 我有什么问题吗?ConcurrentQueue c#,不精确的结果?
class Program
{
static int sum = 0;
static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
static void Main()
{
for (int i = 0; i < 100000; i++)
{
queue.Enqueue(1);
}
Task t1 = Task.Run(() => Calculate());
Task t2 = Task.Run(() => Calculate());
Task.WaitAll(t1, t2);
Console.WriteLine($"Sum = {sum}");
Console.ReadKey();
}
static void Calculate()
{
int result;
while (queue.TryDequeue(out result))
{
sum += result;
}
}
}
答
它,因为
sum += result;
这不是线程安全的。多个线程可以同时触发此LoC并分配相同的值。
您可以通过lock
声明(以及其他方式)修复此问题。
答
这就是问题所在:
sum += result;
这不是原子。这是有效的:
var tmp = sum;
tmp += result;
sum = tmp;
你觉得如果两个你的线程的到达,同时中间线会怎样呢?
您可以Interlocked.Add
解决这个问题:
while (queue.TryDequeue(out result))
{
Interlocked.Add(ref sum, result);
}
注意,这无关使用ConcurrentQueue
- 你会看到同样的事情,如果你的循环适才:
for (int i = 0; i < 50000; i++)
{
sum++; // Just as bad...
}
+0
可以用锁替换联锁吗? – tylkonachwile
+0
@tylkonachwile:是的,那也可以。 –
' sum + = result'不是原子操作,可以由两个线程同时执行。 – Lee