并发与普通集合
本文关键字:集合 并发 | 更新日期: 2023-09-27 18:11:25
我有一个关于System.Collections.Concurrent的问题
我看到并发实际上是一个安全的线程集合,但在哪些情况下它可以是有帮助的?
我做了两个例子,结果是一样的
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++;
}
}
}
现在是正常的Queue:
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和其他一些,但它真正有用的是什么?
任何帮助将非常感激:)
不要将lock()
与ConcurrentQueue<>
或该名称空间中的类似项结合使用。这对表现是有害的。
您可以安全地使用ConcurrentQueue<>
与多个线程,并有很好的性能。对于lock()
和常规集合则不是这样。
这就是为什么你的结果是一样的
使用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);
}
}
当您使用lock
结构时,您的代码有效地按顺序执行,而不是并行执行。此解决方案适用于具有简单Queue
的版本,因为它不是线程安全的,但对于ConcurrentQueue
,使用lock
有点违背了目的。移除ConcurrentQueue
的锁,移除Thread.Sleep
,使用20个线程而不是2个线程。你可以使用Parallel.For()
方法来生成你的任务。
Parallel.For(0, 20, i => Run());
谢谢大家的回答,真的帮了我大忙,我非常感激。
顺便说一下,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);
}
再次感谢:)