2017-04-04 36 views
2

我想問一下關於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; 
     } 
    } 
} 
+0

' sum + = result'不是原子操作,可以由兩個線程同時執行。 – Lee

回答

2

它,因爲

sum += result; 

這不是線程安全的。多個線程可以同時觸發此LoC並分配相同的值。

您可以通過lock聲明(以及其他方式)修復此問題。

7

這就是問題所在:

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:是的,那也可以。 –