线程和列表<>收藏
我有 List
集合,名为 List
。
我有两个线程。 一个线程正在枚举所有列表元素并将其添加到集合中。 第二个线程正在枚举所有列表元素并从中删除。
如何才能保证线程安全呢? 我尝试创建全局对象“MyLock”并在每个线程函数中使用 lock(MyLock) 块,但它不起作用。
你能帮助我吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
如果您有权访问 .NET 4.0,则可以使用类 ConcurrentQueue 或一个 BlockingCollection ,其中ConcurrentQueue 支持它。它完全符合您想要做的事情,并且不需要任何锁定。如果列表中没有可用的项目,BlockingCollection 将使您的线程等待。
从 ConcurrentQueue 中删除的示例,您执行类似的操作
,这将尝试删除一个项目,但如果没有可用的项目,则不会执行任何操作。
这将阻塞并等待
Take
,直到有可以从列表中获取的内容。完成后,您可以调用CompleteAdding(),当列表变空而不是阻塞时,Take 将抛出一个异常。If you have access to .NET 4.0 you can use the class ConcurrentQueue or a BlockingCollection with a ConcurrentQueue backing it. It does exactly what you are trying to do and does not require any locking. The BlockingCollection will make your thread wait if there is no items available in the list.
A example of removing from the ConcurrentQueue you do something like
This will try to remove a item, but if there are none available it does nothing.
This will block and wait on the
Take
till there is something available to take from the list. Once you are done you can callCompleteAdding()
and Take will throw a execption when the list becomes empty instead of blocking.在不了解更多有关您的计划和要求的情况下,我会说这是一个“坏主意”。在迭代列表内容时更改列表很可能会引发异常。
您最好使用
Queue<>
而不是List<>
,因为Queue<>
是通过同步设计的记住。Without knowing more about your program and requirements, I'm going say that this is a "Bad Idea". Altering a
List<>
while iterating through it's contents will most likely throw an exception.You're better off using a
Queue<>
instead of aList<>
, as aQueue<>
was designed with synchronization in mind.您应该能够直接锁定您的列表:
但是,在枚举列表时添加/删除列表可能会导致异常...
You should be able to lock directly on your list:
However adding/removing from the list while enumerating it will likely cause an exception...
锁定
List
的SyncRoot
:有关如何正确使用它的更多信息,请访问 此处
Lock on the
SyncRoot
of yourList<T>
:More information on how to use it properly can be found here
您可以实现自己的
IList
版本,该版本包装底层List
以提供对每个方法调用的锁定。您可以像这样使用此代码:
如果您使用大型列表和/或性能是一个问题,那么这种锁定的性能可能不是很好,但在大多数情况下应该没问题。
请注意,两个
GetEnumerator
方法调用.ToArray()
,这一点非常重要。这会强制在释放锁之前对枚举器进行求值,从而确保对列表的任何修改都不会影响实际的枚举。使用诸如
lock (list) { ... }
或lock (list.SyncRoot) { ... }
之类的代码不会列出枚举期间发生的更改。这些解决方案仅涵盖对列表的并发修改 - 并且前提是所有调用者都在锁内执行此操作。此外,如果某些令人讨厌的代码占用了锁并且不释放它,这些解决方案可能会导致您的代码死亡。在我的解决方案中,您会注意到我有一个对象门,它是我锁定的类内部的私有变量。类之外的任何东西都无法锁定它,因此它是安全的。
我希望这有帮助。
You could implement your own version of
IList<T>
that wraps the underlyingList<T>
to provide locking on every method call.You would use this code like this:
If you're using large lists and/or performance is an issue then this kind of locking may not be terribly performant, but in most cases it should be fine.
It is very important to notice that the two
GetEnumerator
methods call.ToArray()
. This forces the evaluation of the enumerator before the lock is released thus ensuring that any modifications to the list don't affect the actual enumeration.Using code like
lock (list) { ... }
orlock (list.SyncRoot) { ... }
do not cover you against list changes occurring during enumerations. These solutions only cover against concurrent modifications to the list - and that's only if all callers do so within a lock. Also these solutions can cause your code to die if some nasty bit of code takes a lock and doesn't release it.In my solution you'll notice I have a
object gate
that is a private variable internal to the class that I lock on. Nothing outside the class can lock on this so it is safe.I hope this helps.
正如其他人已经说过的,您可以使用 System.Collections.Concurrent 命名空间中的并发集合。如果您可以使用其中之一,那么这是首选。
但如果您确实想要一个刚刚同步的列表,您可以查看
System.Collections.Generic
中的SynchronizedCollection
类。请注意,您必须包含 System.ServiceModel 程序集,这也是我不太喜欢它的原因。但有时我会使用它。
As others already said, you can use concurrent collections from the
System.Collections.Concurrent
namespace. If you can use one of those, this is preferred.But if you really want a list which is just synchronized, you could look at the
SynchronizedCollection<T>
-Class inSystem.Collections.Generic
.Note that you had to include the System.ServiceModel assembly, which is also the reason why I don't like it so much. But sometimes I use it.