我有一个Queue对象,我需要确保它是线程安全的.使用这样的锁对象会更好吗:
lock(myLockObject) { //do stuff with the queue }
或者是否建议像这样使用Queue.Synchronized:
Queue.Synchronized(myQueue).whatever_i_want_to_do();
从阅读MSDN文档开始,我说我应该使用Queue.Synchronized来使它成为线程安全的,但是它给出了一个使用锁对象的例子.来自MSDN文章:
为了保证Queue的线程安全,所有操作必须仅通过此包装器完成.
枚举通过集合本质上不是线程安全的过程.即使集合是同步的,其他线程仍然可以修改集合,这会导致枚举器抛出异常.为了在枚举期间保证线程安全,您可以在整个枚举期间锁定集合,也可以捕获由其他线程所做的更改导致的异常.
如果调用Synchronized()不能确保线程安全有什么意义呢?我在这里错过了什么吗?
我个人总是喜欢锁定.这意味着您可以决定粒度.如果您只依赖于Synchronized包装器,则每个单独的操作都是同步的,但如果您需要执行多个操作(例如,迭代整个集合),则无论如何都需要锁定.为了简单起见,我宁愿只记住一件事 - 适当锁定!
编辑:如评论中所述,如果您可以使用更高级别的抽象,那就太棒了.如果您确实使用了锁定,请小心它 - 记录您希望锁定的位置,并在尽可能短的时间内获取/释放锁定(更多的是正确性而不是性能).避免在持有锁时调用未知代码,避免嵌套锁等.
在.NET 4中有一个很大的更高层次的抽象(包括无锁码)更多的支持.无论哪种方式,我仍然不建议使用同步包装器.
Synchronized
旧集合库中的方法存在一个主要问题,即它们的粒度级别太低(每个方法而不是每个工作单元).
有一个带有同步队列的经典竞争条件,如下所示,您可以检查Count
它是否可以出列,但是该Dequeue
方法会抛出一个异常,指示队列为空.发生这种情况是因为每个单独的操作都是线程安全的,但是在Count
查询它和使用该值时,值可以更改.
object item; if (queue.Count > 0) { // at this point another thread dequeues the last item, and then // the next line will throw an InvalidOperationException... item = queue.Dequeue(); }
您可以使用围绕整个工作单元的手动锁(即检查计数和出列项目)安全地编写此内容,如下所示:
object item; lock (queue) { if (queue.Count > 0) { item = queue.Dequeue(); } }
因此,您无法安全地从同步队列中出列任何内容,我不会打扰它,只会使用手动锁定.
.NET 4.0应该有一大堆正确实现的线程安全集合,但不幸的是,这仍然将近一年.
"线程安全集合"的需求与以原子方式对集合执行多个操作的要求之间经常存在紧张关系.
因此,Synchronized()为您提供了一个集合,如果多个线程同时向其添加项目,它将不会粉碎自己,但它并不会神奇地为您提供一个集合,它知道在枚举期间,没有其他人必须触摸它.
以及枚举,像常见的操作"是这个项目已经在排队吗?没有的话,我会添加它"还要求同步,比刚才队列宽.
这样我们就不需要锁定队列就会发现它是空的.
object item; if (queue.Count > 0) { lock (queue) { if (queue.Count > 0) { item = queue.Dequeue(); } } }