在我开始一个项目之前,我编写了一个简单的测试来比较来自(System.Collections.Concurrent)的ConcurrentBag相对于锁定和列表的性能.我非常惊讶ConcurrentBag比使用简单的List锁定慢10倍.据我所知,当读写器是同一个线程时,ConcurrentBag效果最好.但是,我没想到它的性能会比传统的锁更糟糕.
我已经运行了一个测试,其中有两个Parallel for循环写入和读取列表/包.然而,写入本身显示了巨大的差异:
private static void ConcurrentBagTest()
{
int collSize = 10000000;
Stopwatch stopWatch = new Stopwatch();
ConcurrentBag<int> bag1 = new ConcurrentBag<int>();
stopWatch.Start();
Parallel.For(0, collSize, delegate(int i)
{
bag1.Add(i);
});
stopWatch.Stop();
Console.WriteLine("Elapsed Time = {0}",
stopWatch.Elapsed.TotalSeconds);
}
Run Code Online (Sandbox Code Playgroud)
在我的盒子上,这需要3-4秒才能运行,相比之下这段代码的0.5-0.9秒:
private static void LockCollTest()
{
int collSize = 10000000;
object list1_lock=new object();
List<int> lst1 = new List<int>(collSize);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Parallel.For(0, collSize, delegate(int i)
{
lock(list1_lock)
{
lst1.Add(i);
}
});
stopWatch.Stop();
Console.WriteLine("Elapsed = {0}",
stopWatch.Elapsed.TotalSeconds);
}
Run Code Online (Sandbox Code Playgroud)
正如我所提到的,进行并发读写并不能帮助并发包测试.我做错了什么还是这个数据结构真的很慢?
[编辑] - …
之前,我甚至问,让我得到了明显的答案的方式进行:该ICollection<T>接口包括Remove删除任意元素,该方法Queue<T>并Stack<T>不能真正支持(因为他们只能删除"结束"元素).
好的,我意识到了.实际上,我的问题并不是关于Queue<T>或Stack<T>收集类型; 更确切地说,它是关于不执行的设计决定ICollection<T>了任何泛型类型是基本的集合T值.
这是我觉得奇怪的事情.假设我有一个接受任意集合的方法T,并且出于我正在编写的代码的目的,知道集合的大小会很有用.例如(以下代码是微不足道的,仅供参考!):
// Argument validation omitted for brevity.
static IEnumerable<T> FirstHalf<T>(this ICollection<T> source)
{
int i = 0;
foreach (T item in source)
{
yield return item;
if ((++i) >= (source.Count / 2))
{
break;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,除了那些类型没有实现之外,没有理由为什么这个代码不能在a Queue<T>或a 上运行.当然,他们确实实现了 - 我主要是为了单独测试属性 - 但是这会导致像这样的奇怪的优化代码:Stack<T>ICollection<T>ICollectionCount
// OK, so to accommodate …Run Code Online (Sandbox Code Playgroud)