2015-09-07 34 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); 
    } 

再次感谢:)