2015-09-07 31 views
1

,所以我有一個關於System.Collections.Concurrent併發vs普通收藏?

我看到併發是acctually安全線程收集的問題,但在至極情況下,它可以是有益的?

我製成2個實施例和結果是相同的

首先ConcurrentQueue:

static ConcurrentQueue<int> queue = new ConcurrentQueue<int>(); 
    private static readonly object obj = new object(); 
    static int i = 0; 
    static int Num = 0; 
    static void Run(object loopNum) 
    { 
     lock (obj) 
     { 
      for (int N = 0; N < 10; N++) 
      { 
       queue.Enqueue (i); 
       Thread.Sleep(250); 
       queue.TryDequeue(out Num); 
       Console.WriteLine($"{Num} Added! in {loopNum} Loop, ThreadID: [{Thread.CurrentThread.ManagedThreadId}]"); 
       i++; 
      } 
     } 
    } 

而現在的正常隊列:

static Queue<int> queue = new Queue<int>(); 
    private static readonly object obj = new object(); 
    static int i = 0; 

    static void Run(object loopNum) 
    { 
     lock (obj) 
     { 
      for (int N = 0; N < 10; N++) 
      { 
       queue.Enqueue (i); 
       Thread.Sleep(250); 
       Console.WriteLine($"{queue.Dequeue()} Added! in {loopNum} Loop, ThreadID: [{Thread.CurrentThread.ManagedThreadId}]"); 
       i++; 
      } 
     } 
    } 

主要:

static void Main() 
    { 
     Thread[] Th = new Thread[] { new Thread(Run), new Thread(Run) }; 
     Th[0].Start("First"); 
     Th[1].Start("Second"); 


     Console.ReadKey(); 
    } 

結果a重新相同

當然,它有一些像TryDequeue一樣的不同方法,還有一些,但它真的有幫助嗎?

任何幫助將非常appriciated :)

+2

有用。 – Jodrell

+1

當試圖測量不同線程安全機制之間的時間差時,添加250ms的延遲可能會超過該差異一些數量級...... – GolfWolf

回答

2

不要連同該命名空間ConcurrentQueue<>或類似物品使用lock()。這對性能不利。

您可以安全地使用ConcurrentQueue<>多線程,並有很好的性能。與lock()和定期收藏不能說。

這就是爲什麼你的結果是相同的。

+0

請注意,訪問共享資源的其他代碼也必須是線程安全的 - 在此如果用'Interlocked.Increment(ref i)'替換'i ++'將會有效。當然,'Thread.Sleep'中的250ms等待可能會使用一個簡單的隊列使兩個線程的性能差異變得更小......增加更多的線程並擺脫'Thread.Sleep'會畫出更清晰的圖片。 – Luaan

1

當您使用lock構造時,您的代碼將按順序執行,而不是並行執行。此解決方案適用於簡單的Queue版本,因爲它不是線程安全的,但使用ConcurrentQueue時,使用lock有點違背了目的。刪除ConcurrentQueue的鎖,刪除Thread.Sleep,並使用20個線程而不是2個僅用於踢球。你可以使用Parallel.For()方法產生你的任務。

Parallel.For(0, 20, i => Run()); 
2

使用ConcurrentQueue<T>的原因是爲了避免編寫自己的鎖定代碼。

如果您有多個線程添加或刪除Queue<T>中的項目,您可能會遇到異常。使用ConcurrentQueue<T>將避免例外。

下面是一個示例程序使用多個線程寫入Queue<T>,同時其與ConcurrentQueue<T>工作時可能會導致異常:

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Threading.Tasks; 

internal class Program 
{ 
    private static void Main() 
    { 
     var queue1 = new ConcurrentQueue<int>(); 
     var queue2 = new Queue<int>(); 

     // This will work fine. 

     var task1 = Task.Run(() => producer(item => queue1.Enqueue(item))); 
     var task2 = Task.Run(() => producer(item => queue1.Enqueue(item))); 

     Task.WaitAll(task1, task2); 

     // This will cause an exception. 

     var task3 = Task.Run(() => producer(item => queue2.Enqueue(item))); 
     var task4 = Task.Run(() => producer(item => queue2.Enqueue(item))); 

     Task.WaitAll(task3, task4); 
    } 

    private static void producer(Action<int> add) 
    { 
     for (int i = 0; i < 10000; ++i) 
      add(i); 
    } 
} 

試着運行一下,看看會發生什麼。

0

謝謝大家的所有答案,真的幫了我,我appriciate它很多。

順便說一下,馬修華生,你的榜樣有時會給我們一個例外,有時候不是,我做了一個更好的例子,但是我明白了。當收集的許多線程併發訪問

private static void Main() 
    { 
     var queue1 = new ConcurrentQueue<int>(); 
     var queue2 = new Queue<int>(); 

     // This will work fine. 

     var task1 = Enumerable.Range(0, 40) 
      .Select(_ => Task.Run(() => producer(item => queue1.Enqueue(item)))) 
      .ToArray(); 

     Task.WaitAll(task1); 

     // This will cause an exception. 

     var task2 = Enumerable.Range(0, 40) 
         .Select(_ => Task.Run(() => producer(item => queue2.Enqueue(item)))) 
         .ToArray(); 

     Task.WaitAll(task2); 
    } 

再次感謝:)