3.2<3.4<3.7( )<( )怒之铁拳4.01b>4.008>4.006>( )>( )

Before I started a project, I wrote a simple test to compare the performance of ConcurrentBag from (System.Collections.Concurrent) relative to locking & lists. I am extremely surprised that ConcurrentBag is over 10 times slower than locking with a simple List. From what I understand, the ConcurrentBag works best when the reader and writer is the same thread. However, I hadn't thought it's performance would be so much worse than traditional locks.
I have run a test with two Parallel for loops writing to and reading from a list/bag. However, the write by itself shows a huge difference:
private static void ConcurrentBagTest()
int collSize = ;
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);
On my box, this takes between 3-4 secs to run, compared to 0.5 - 0.9 secs of this code:
private static void LockCollTest()
int collSize = ;
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);
As I mentioned, doing concurrent reads and writes doesn't help the concurrent bag test. Am I doing something wrong or is this data structure just really slow?
[EDIT] - I removed the Tasks because I don't need them here (Full code had another task reading)
Thanks a lot for the answers. I am having a hard time picking "the right answer" since it seems to be a mix of a few answers.
As Michael Goldshteyn pointed out, the speed really depends on the data.
Darin pointed out that there should be more contention for ConcurrentBag to be faster, and Parallel.For doesn't necessarily start the same number of threads. One point to take away is to not do anything you don't have to inside a lock. In the above case, I don't see myself doing anything inside the lock except may be assigning the value to a temp variable.
Additionally, sixlettervariables pointed out that the number of threads that happen to be running may also affect results, although I tried running the original test in reverse order and ConcurrentBag was still slower.
I ran some tests with starting 15 Tasks and the results depended on the collection size among other things. However, ConcurrentBag performed almost as well as or better than locking a list, for up to 1 million insertions. Above 1 million, locking seemed to be much faster sometimes, but I'll probably never have a larger datastructure for my project.
Here's the code I ran:
int collSize = 1000000;
object list1_lock=new object();
List&int& lst1 = new List&int&();
ConcurrentBag&int& concBag = new ConcurrentBag&int&();
int numTasks = 15;
int i = 0;
Stopwatch sWatch = new Stopwatch();
sWatch.Start();
//First, try locks
Task.WaitAll(Enumerable.Range(1, numTasks)
.Select(x =& Task.Factory.StartNew(() =&
for (i = 0; i & collSize / numT i++)
lock (list1_lock)
lst1.Add(x);
})).ToArray());
sWatch.Stop();
Console.WriteLine("lock test. Elapsed = {0}",
sWatch.Elapsed.TotalSeconds);
// now try concurrentBag
sWatch.Restart();
Task.WaitAll(Enumerable.Range(1, numTasks).
Select(x =& Task.Factory.StartNew(() =&
for (i = 0; i & collSize / numT i++)
concBag.Add(x);
})).ToArray());
sWatch.Stop();
Console.WriteLine("Conc Bag test. Elapsed = {0}",
sWatch.Elapsed.TotalSeconds);
解决方案 Let me ask you this: how realistic is it that you'd have an application which is constantly adding to a collection and never reading from it? What's the use of such a collection? (This is not a purely rhetorical question. I could imagine there being uses where, e.g., you only read from the collection on shutdown (for logging) or when requested by the user. I believe these scenarios are fairly rare, though.)
This is what your code is simulating. Calling List&T&.Add is going to be lightning-fast in all but the occasional case where the list has to resiz but this is smoothed out by all the other adds that happen quite quickly. So you're not likely to see a significant amount of contention in this context, especially testing on a personal PC with, e.g., even 8 cores (as you stated you have in a comment somewhere). Maybe you might see more contention on something like a 24-core machine, where many cores can be trying to add to the list literally at the same time.
Contention is much more likely to creep in where you read from your collection, esp. in foreach loops (or LINQ queries which amount to foreach loops under the hood) which require locking the entire operation so that you aren't modifying your collection while iterating over it.
If you can realistically reproduce this scenario, I believe you will see ConcurrentBag&T& scale much better than your current test is showing.
is a program I wrote to compare these collections in the scenario I described above (multiple writers, many readers). Running 25 trials with a collection size of 10000 and 8 reader threads, I got the following results:
Took 529.0095 ms to add 10000 elements to a List&double& with 8 reader threads.
Took 39.5237 ms to add 10000 elements to a ConcurrentBag&double& with 8 reader threads.
Took 309.4475 ms to add 10000 elements to a List&double& with 8 reader threads.
Took 81.1967 ms to add 10000 elements to a ConcurrentBag&double& with 8 reader threads.
Took 228.7669 ms to add 10000 elements to a List&double& with 8 reader threads.
Took 164.8376 ms to add 10000 elements to a ConcurrentBag&double& with 8 reader threads.
Average list time: 176.072456 ms.
Average bag time: 59.603656 ms.
So clearly it depends on exactly what you're doing with these collections.
本文地址: &
在我启动了一个项目,我写了一个简单的测试,以ConcurrentBag从(System.Collections.Concurrent)相对性能比较锁定和放大器;名单。我感到非常惊讶,ConcurrentBag比用一个简单的列表锁定慢10倍以上。据我了解,在ConcurrentBag效果最好时,读写器是相同的线程。但是,我没有想到它的性能会比传统的锁差这么多。
我对循环写入和列表/袋读运行一个测试有两个平行。然而,本身写入显示出巨大的差异:
私有静态无效ConcurrentBagTest()
INT collSize =千万;
秒表秒表=新的秒表();
ConcurrentBag< INT> BAG1 =新ConcurrentBag<诠释>();
stopWatch.Start();
的Parallel.For(0,collSize,委托(int i)以
bag1.Add(ⅰ);
stopWatch.Stop();
Console.WriteLine(“经历时间= {0}”,
stopWatch.Elapsed.TotalSeconds);
在我的盒子,这需要3-4秒之间运行,相比于0.5
0.9秒的这个code:
私有静态无效LockCollTest()
INT collSize =千万;
对象list1_lock =新的对象();
名单< INT> LST1 =新的名单,其中,INT>(collSize);
秒表秒表=新的秒表();
stopWatch.Start();
的Parallel.For(0,collSize,委托(int i)以
锁(list1_lock)
lst1.Add(ⅰ);
stopWatch.Stop();
Console.WriteLine(“经历= {0}”,
stopWatch.Elapsed.TotalSeconds);
正如我所提到的,在做并行读取和写入不利于并发包测试。我是不是做错了什么或者是这个数据结构真的很慢?
- 我删除了任务,因为我不需要他们在这里(满code的另一项任务读数)
非常感谢你的答案。我有一个很难挑选“正确答案”,因为它似乎是一些答案的组合。
正如迈克尔·Goldshteyn指出,速度实际上取决于数据。
达林指出应该有更多的争用ConcurrentBag要更快,而的Parallel.For不一定启动线程的数相同。有一点要带走的是不要做任何你不知道的必须锁里面。在上述情况下,我没有看到,除了可能的值赋给一个临时变量自己做什么锁里面。
此外,sixlettervariables指出线程碰巧运行的数目也可能影响的结果,尽管我试图运行在相反的顺序原测试和ConcurrentBag仍然较慢
我进行了一些测试,开始15任务和结果取决于除其他事项外集合大小。然而,ConcurrentBag表现几乎一样好,甚至比锁定列表,多达100万插入更好。超过100万,锁定似乎要快很多的时候,但是我可能永远不会有我的项目更大的数据结构。
这里的code,我跑:
INT collSize = 1000000;
对象list1_lock =新的对象();
名单< INT> LST1 =新的名单,其中,INT>();
ConcurrentBag< INT> concBag =新ConcurrentBag<诠释>();
INT numTasks = 15;
INT I = 0;
秒表斯沃琪=新的秒表();
sWatch.Start();
//首先,尝试锁
Task.WaitAll(Enumerable.Range(1,numTasks)
。选择(X => Task.Factory.StartNew(()=>
对于(i = 0; I< collSize / numT我++)
锁(list1_lock)
lst1.Add(X);
}))的ToArray())。
sWatch.Stop();
Console.WriteLine(“锁测试。消逝= {0}”,
sWatch.Elapsed.TotalSeconds);
//现在尝试concurrentBag
sWatch.Restart();
Task.WaitAll(Enumerable.Range(1,numTasks)。
选择(X => Task.Factory.StartNew(()=>
对于(i = 0; I< collSize / numT我++)
concBag.Add(X);
}))的ToArray())。
sWatch.Stop();
Console.WriteLine(“浓袋的考验。消逝= {0}”,
sWatch.Elapsed.TotalSeconds);
解决方案 让我问你:怎样的现实是,你不得不被不断添加到集合并再从中读取应用程序?有什么用这样一个集合的? (这不是一个纯粹的反问。我的可以的想象有被使用的地方,例如,你只能从收集阅读关机(用于记录),或者当用户请求。我相信这些方案是相当罕见,虽然。)
这是你的code为模拟。呼叫名单,其中,T>。新增将是闪电般的速度,但在所有的偶然情况下列表有来调整其内部数组;但是这是平滑由所有其他补充说相当迅速发生。所以,你可能不会看到在这个背景下显著量争,尤其的个人PC机上的测试,例如,即使是8个内核(如你说你有一个注释的地方)。的也许的,你可能会看到的东西像一个24核的机器,有许多内核可尝试添加到列表中的字面上的在同一时间。更多争
争更可能在你的悄悄从您的收藏读的,尤其。在的foreach 循环(或LINQ查询其金额的foreach 引擎盖下环),需要锁定整个运作,使你是不是修改你的收藏,同时进行迭代。
如果你能真实地再现这种情况下,我相信你会看到 ConcurrentBag< T> 规模比你目前的测试更好的展示
更新:是一个程序,我写的比较这些集合在我上面描述的场景(多作家,不少读者)。运行25试验与10000和8个读线程集合的大小,我得到了以下结果:
花了529.0095毫秒增加10000元素到一个List<双> 8读取器线程。
拿了39.5237毫秒增加10000元到ConcurrentBag<双> 8读取器线程。
花了309.4475毫秒增加10000元素到一个List<双> 8读取器线程。
拿了81.1967毫秒增加10000元到ConcurrentBag<双> 8读取器线程。
花了228.7669毫秒增加10000元素到一个List<双> 8读取器线程。
花了164.8376毫秒增加10000元到ConcurrentBag<双> 8读取器线程。
平均上榜时间:176.072456毫秒
一般的包包时间:59.603656毫秒
所以很明显这取决于你在做什么与这些集合。
本文地址: &
扫一扫关注官方微信使用 .NET 4.0 中的Lazy&T& 实现延迟初始化
延迟初始化就是将对象的初始化延迟到第一次使用该对象时。延迟初始化是我们在写程序时经常会遇到的情形,例如创建某一对象时需要花费很大的开销,而这一对象在的运行过程中不一定会用到,这时就可以使用延迟初始化,在第一次使用该对象时再对其进行初始化,使用延迟初始化可以提高程序的效率,使程序占用更少的内存。在 .NET 4.0 之前要实现延迟初始化,需要我们自己动手编写具体的实现方式(关于延迟初始化的实现方式,可以参看这个 /en-us/vcsharp/bb870976.aspx& .NET 4.0 的 Lazy&T& 的实现方式与此相似)。在 .NET 4.0 中提供了一个泛型类 System.Lazy&T& 可以帮助我们实现延迟初始化。
首先我们来看下面的代码:
view sourceprint?01 class Program&
03&&&&&&& static void Main(string[] args)&
04&&&&&&& {&
05&&&&&&&&&&& Lazy&Customer& customer = new Lazy&Customer&();&
06&&&&&&&&&&& if (!customer.IsValueCreated)&
07&&&&&&&&&&&&&&& Console.WriteLine("Customer 尚未初始化。");&
09&&&&&&&&&&& Console.WriteLine("客户姓名是:{0}", customer.Value.Name);&
11&&&&&&&&&&& if (customer.IsValueCreated)&
12&&&&&&&&&&&&&&& Console.WriteLine("Customer 已经初始化。");&
14&&&&&&&&&&& Console.ReadKey();&
15&&&&&&& }&
18&&& public class Customer&
20&&&&&&& public Customer()&
21&&&&&&& {&
22&&&&&&&&&&& Name = "张三";&
23&&&&&&&&&&& Console.WriteLine("调用 Customer 的构造函数。");&
24&&&&&&& }&
26&&&&&&& public string Name { }&
28&&&&&&& public List&Order& Orders { }&
31&&& public class Order&
33&&&&&&& public string ID { }&
下面是输出结果:
Customer 尚未初始化。 调用 Customer 的构造函数。 客户姓名是:张三 Customer 已经初始化。
当 customer.Value 被调用时 Customer 才被初始化。只读属性 Value 返回延迟初始化的对象,布尔属性 IsValueCreated 标识延迟初始化的对象是否已经初始化。在上面的示例代码中是在 Customer 类的构造函数中设置的 Name 属性的值,我们可以使用 Lazy&T& 的重载函数 Lazy& T& (Func& T& ) 传入一个带返回值的委托来设置延迟初始化对象的属性值。
view sourceprint?1 Lazy&Customer& customer1 = new Lazy&Customer&(&
2&&&& () =&&
4&&&&&&&& return new Customer { Name = "李四" };&
以下是输出结果:
Customer 尚未初始化。 调用 Customer 的构造函数。 客户姓名是:李四 Customer 已经初始化。
可以看到 customer1 的 Value 属性使用的是通过委托传入的参数初始化的。
要实现公共属性的延迟初始化,将属性对应的字段定义为 Lazy&T&,然后通过 get 访问器返回字段的 Value 属性。
view sourceprint?01 public class Customer&
03&&&&&&&& public Customer()&
04&&&&&&&& {&
05&&&&&&&&&&&& Name = "张三";&
06&&&&&&&&&&&& Console.WriteLine("调用 Customer 的构造函数。");&
08&&&&&&&&&&&& _orders = new Lazy&List&Order&&(() =&&
09&&&&&&&&&&&&&&&& {&
10&&&&&&&&&&&&&&&&&&&& // 初始化&
11&&&&&&&&&&&&&&&&&&&& return new List&Order&&
12&&&&&&&&&&&&&&&&&&&& {&
13&&&&&&&&&&&&&&&&&&&&&&&& new Order { ID = "1" },&
14&&&&&&&&&&&&&&&&&&&&&&&& new Order { ID = "2" },&
15&&&&&&&&&&&&&&&&&&&&&&&& new Order { ID = "3" }&
16&&&&&&&&&&&&&&&&&&&& };&
17&&&&&&&&&&&&&&&& }&
18&&&&&&&&&&&& );&
19&&&&&&&& }&
21&&&&&&&& public string Name { }&
23&&&&&&&& private Lazy&List&Order&& _&
24&&&&&&&& public List&Order& Orders&
25&&&&&&&& {&
26&&&&&&&&&&&& get
27&&&&&&&&&&&& {&
28&&&&&&&&&&&&&&&& return _orders.V // 将在第一次访问时创建&
29&&&&&&&&&&&& }&
30&&&&&&&& }&
33&&&& public class Order&
35&&&&&&&& public string ID { }&
Lazy&T& 对象初始化默认是线程安全的,在多线程环境下,第一个访问 Lazy&T& 对象的 Value 属性的线程将初始化 Lazy&T& 对象,以后访问的线程都将使用第一次初始化的数据。
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'}

我要回帖

更多关于 lt gt 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信