所以我对System.Collections.Concurrent
有疑问我看到Concurrent实际上是一个安全的线程集合,但在哪种情况下它可以有用吗?
我做了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();
}
结果相同
当然,它有一些不同的方法,比如TryDequeue和更多,但它真正有用的是什么?
任何帮助都会非常贴切:)
答案 0 :(得分:2)
不要将lock()
与ConcurrentQueue<>
或该命名空间中的类似项一起使用。这对性能有害。
您可以安全地使用ConcurrentQueue<>
多个线程并具有出色的性能。 lock()
和常规集合不能说同样的内容。
这就是为什么你的结果是一样的。
答案 1 :(得分: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);
}
}
尝试运行它,看看会发生什么。
答案 2 :(得分:1)
当您使用lock
构造时,您的代码将按顺序有效执行,而不是并行执行。此解决方案适用于简单Queue
的版本,因为它不是线程安全的,但使用ConcurrentQueue
,使用lock
有点失败了。移除ConcurrentQueue
的锁定,移除Thread.Sleep
,并使用20个线程而不是2个线程仅用于踢。您可以使用Parallel.For()
方法生成任务。
Parallel.For(0, 20, i => Run());
答案 3 :(得分:0)
谢谢大家的所有答案,真的帮助了我,我很好地批评了它。
顺便说一下Matthew Watson,你的例子有时会给出一个例外,有时候不是,我做了一个更好的例子,但是我明白了。
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);
}
再次感谢:)