Lock 与 ToArray 的线程安全 foreach 访问列表集合 [英] Lock vs. ToArray for thread safe foreach access of List collection

查看:24
本文介绍了Lock 与 ToArray 的线程安全 foreach 访问列表集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 List 集合,我想在多线程应用程序中对其进行迭代.我每次迭代时都需要保护它,因为它可以更改,而且我不希望在执行 foreach 时出现集合已修改"异常.

I've got a List collection and I want to iterate over it in a multi threaded app. I need to protect it every time I iterate it since it could be changed and I don't want "collection was modified" exceptions when I do a foreach.

这样做的正确方法是什么?

What is the correct way to do this?

  1. 每次访问或循环时使用锁.我比较害怕死锁.也许我只是偏执使用锁,不应该.如果我走这条路线以避免死锁,我需要知道什么?锁定是否相当有效?

  1. Use lock every time I access or loop. I'm rather terrified of deadlocks. Maybe I'm just paranoid of using lock and shouldn't be. What do I need to know if I go this route to avoid deadlocks? Is lock fairly efficient?

每次执行 foreach 时,使用 List<>.ToArray() 复制到数组.这会导致性能下降,但很容易做到.我担心内存颠簸以及复制它的时间.只是显得过分了.使用 ToArray 线程安全吗?

Use List<>.ToArray() to copy to an array each time I do a foreach. This causes a performance hit but is easy to do. I'm worried about memory thrashing as well as the time to copy it. Just seems excessive. Is it thread safe to use ToArray?

不要使用 foreach 而是使用 for 循环.每次执行此操作时我是否都需要进行长度检查以确保列表不会缩小?这看起来很烦人.

Don't use foreach and use for loops instead. Wouldn't I need to do a length check every time I did this to make sure the list didn't shrink? That seems annoying.

推荐答案

没有理由害怕死锁,它们很容易被发现.你的程序停止运行,死掉的赠品.你真正应该害怕的是线程竞争,当你应该锁定时不锁定你会得到的那种错误.非常难以诊断.

There's little reason to be afraid of deadlocks, they are easy to detect. Your program stops running, dead giveaway. What you really should be terrified of is threading races, the kind of bug you'll get when you don't lock when you should. Very hard to diagnose.

  1. 使用锁没问题,只要确保在涉及该列表的任何代码中使用完全相同的锁定对象即可.就像从该列表中添加或删除项目的代码一样.如果该代码在迭代列表的同一线程上运行,则您不需要锁定.通常,这里发生死锁的唯一机会是如果您的代码依赖于线程状态,例如 Thread.Join(),同时它也持有该锁定对象.这应该很少见.

  1. Using lock is fine, just make sure you use the exact same locking object in any code that touches that list. Like the code that adds or removes items from that list. If that code runs on the same thread that iterates the list then you don't need a lock. Generally, the only chance for deadlock here is if you have code that relies on the thread state, like Thread.Join(), while it is also holding that locking object. Which ought to be rare.

是的,迭代列表的副本始终是线程安全的,只要您在 ToArray() 方法周围使用锁即可.请注意,您仍然需要锁,没有结构改进.优点是您将在短时间内保持锁定,从而提高程序的并发性.缺点是它的 O(n) 存储要求,只有一个安全列表,但不能保护列表中的元素,以及始终拥有列表内容的陈旧视图的棘手问题.尤其是最后一个问题很微妙,很难分析.如果您无法推断出副作用,那么您可能不应该考虑这一点.

Yes, iterating a copy of the list is always thread-safe, as long as you use a lock around the ToArray() method. Note that you still need the lock, no structural improvement. The advantage is that you'll hold the lock for a short amount of time, improving concurrency in your program. The disadvantages are its O(n) storage requirements, only having a safe list but not protecting the elements in the list and the tricky problem of always having a stale view of the list content. Especially the last problem is subtle and hard to analyze. If you cannot reason out the side-effects then you probably shouldn't consider this.

务必将 foreach 检测种族的能力视为礼物,而不是问题.是的,一个显式的 for(;;) 循环不会抛出异常,它只会出现故障.就像重复相同的项目两次或完全跳过一个项目.您可以通过向后迭代来避免重新检查项目数量.只要其他线程只调用 Add() 而不是 Remove() 行为与 ToArray() 类似,您就会得到陈旧的视图.并不是说这在实践中会起作用,索引列表也不是线程安全的.List<> 将在必要时重新分配其内部数组.这只是行不通,并且会以不可预测的方式出现故障.

Do make sure to treat the ability of foreach to detect a race as a gift, not a problem. Yes, an explicit for(;;) loop is not going to throw the exception, it is just going to malfunction. Like iterating the same item twice or skipping an item completely. You could avoid having to re-check the number of items by iterating it backwards. As long as other thread(s) are only calling Add() and not Remove() that would behave similarly to ToArray(), you'll get the stale view. Not that this will work in practice, indexing the list is not thread-safe either. List<> will reallocate its internal array if necessary. This just won't work and malfunction in unpredictable ways.

这里有两个观点.你可能会害怕并遵循常识,你会得到一个有效但可能不是最佳的程序.这是明智的,让老板高兴.或者,您可以进行实验并亲自找出规则的偏差如何给您带来麻烦.这会让你开心,你会成为一个更好的程序员.但是你的生产力会受到影响.我不知道你的日程安排是什么样的.

There are two points of view here. You can be terrified and follow common wisdom, you'll get a program that works but might not be optimal. That's wise and keeps the boss happy. Or you can experiment and find out for yourself how skewing the rules gets you in trouble. Which will make you happy, you'll be a much better programmer. But your productivity is going to suffer. I don't know what your schedule looks like.

这篇关于Lock 与 ToArray 的线程安全 foreach 访问列表集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆