【问题标题】:.net 4.0 Concurrent Collection's performance.net 4.0 并发集合的性能
【发布时间】:2012-10-26 09:08:45
【问题描述】:

如果我们有一个装满 100 个对象的 ConcurrentBag<object>safeBag`。

然后一个线程作为:

   foreach(object o in safeBag)
{
    Thread.Sleep(1000);
}

另一个线程在第一个线程启动后立即启动:

{
    safeBag.AddOrTake(something);
}

第二个线程会等待 100 秒进入资源吗? 另一个问题,如果第一个线程使用 Parallel.ForEach() 运行,线程将如何工作?

编辑:MSDN 说:“列表可以同时支持多个读取器,只要不修改集合。通过集合枚举本质上不是线程安全的过程。在极少数情况下,枚举与一个或更多写访问,确保线程安全的唯一方法是在整个枚举期间锁定集合。”通过 ConcurrentBag 进行枚举是否会导致第二个线程等待对 ConcurrentBag 的写入访问?

【问题讨论】:

  • 第一个问题:您是否尝试使用少量 Console.WriteLine() 运行测试程序?
  • 第二个问题:这取决于您在 Parallel.ForEach 中执行的操作。再次,抱歉坚持,先尝试再问你不懂的地方(附上完整的运行示例)

标签: c# .net concurrency foreach


【解决方案1】:

对于大多数Concurrent* 集合,大多数操作都是原子的,但不持有任何长期锁定。 GetEnumerator() 返回后,第一个线程不会阻塞第二个线程。

ConcurrentBag<T>.GetEnumerator Method

枚举表示包内容的即时快照。它不反映调用 GetEnumerator 后对集合的任何更新。枚举器可以安全地与对包的读取和写入同时使用。

【讨论】:

    【解决方案2】:

    第二个线程,假设您连续生成两个线程 - 第一个使用 ThreadStart 指向包含迭代的块,第二个指向另一个代码块,不会等待 1000 毫秒。 foreach 块将在移动到集合中的下一个对象之间仅等待 1 秒,第二个块不受此影响。

    如果它是一个并行的 foreach,那么在移动到下一个元素之前,您将有多个线程(同时)等待一秒钟。第二个区块仍然不会等待ConcurrentBag 变为空闲。

    【讨论】:

    • MSDN 说:“一个 List 可以同时支持多个读取器,只要不修改集合即可。通过集合枚举本质上不是线程安全的过程。在极少数情况下枚举与一个或多个写访问竞争,确保线程安全的唯一方法是在整个枚举期间锁定集合。”通过 ConcurrentBag 进行枚举是否会导致第二个线程等待对 ConcurrentBag 的写入访问?
    • @tianyi 为什么在使用CuncurrentBag<T> 时引用List<T> 的文档?
    • @mlorbetske 不,ConcurrentBag 被授予线程安全的(即使对于枚举),因此如果您在枚举期间修改集合,它不会引发任何异常。并发也意味着它不会锁定任何东西(问题不在于 MoveNext())。此外,线程安全并不意味着您将获得预期的结果(它会创建集合的副本,您将枚举副本,因此您不会看到任何更改)。
    • @Adriano,我更笼统地说,修改您正在迭代的集合是一个坏主意(因此逻辑上不一致的部分 - 关于并发集合)
    • @mlorbetske hmmmm 实际上对于“正常”集合,这不仅是一个坏主意,而且您不能这样做。使用 ConcurrentBag 这不是一个坏主意(但您必须注意它的行为,因为它也对性能有影响)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多