当前位置: 代码迷 >> C# >> lock嵌套使用有什么目的(c#)?该如何解决
  详细解决方案

lock嵌套使用有什么目的(c#)?该如何解决

热度:34   发布时间:2016-05-05 04:47:33.0
lock嵌套使用有什么目的(c#)?
近日,看到一开源项目中有以下函数:

public cGatherUrlItem Dequeue()
        {
            cGatherUrlItem item = null;
            lock (((ICollection)MainUrls).SyncRoot)
            {
                if (this.MainUrls.Count > 0)
                {
                    lock (((ICollection)MainUrls).SyncRoot)
                    {
                        item = MainUrls.Dequeue();
                        return item;
                    }
                }
            }
            lock (((ICollection)RunUrls).SyncRoot)
            {
                if (this.RunUrls.Count > 0)
                {
                    lock (((ICollection)RunUrls).SyncRoot)
                    {
                        item = RunUrls.Dequeue();
                        return item;
                    }
                }
            }
            return item;
        }

小弟十分迷惑,被lock锁住的代码还有什么必要再嵌套一层lock呢,而且两个lock锁的是相同的对象,这样有什么用意呢?
------解决思路----------------------
            lock (((ICollection)MainUrls).SyncRoot)
            {
                if (this.MainUrls.Count > 0)
                {
                    lock (((ICollection)MainUrls).SyncRoot)
                    {
                        item = MainUrls.Dequeue();
                        return item;
                    }
                }
            }

这类代码估计是把知识死记硬背给搞“反”了。

通常是这样写

if (this.MainUrls.Count > 0)
{
       lock (((ICollection)MainUrls).SyncRoot)
       {
                if (this.MainUrls.Count > 0)
                {
                        item = MainUrls.Dequeue();
                        return item;
                }
      }
}
也就是说,写两个if判断。因为在外边的if判断成立之后,lock语句未完成之前,MainUrls 集合仍有可能被修改(清空),所以lock里边需要再判断一下。

而写两个lock,完全是不可理喻的。估计是道听途说上述描述,给“搞反了”意思。
------解决思路----------------------
“被lock锁住的代码还有什么必要再嵌套一层lock呢”这个说法严格说来不正确,至少会引起许多歧义。

并没有什么“被锁住的代码”的概念。锁其实就相当于交通“红绿灯”,而不是一个真正给一个道路“上锁”。如果有人不看红绿灯,那么也就闯过去了,并不会被红绿灯给撞回来。所以lock确实会有多个连缀的情况,因为锁不可能“锁住代码”,只有关心同样的lock对象的代码才会阻塞,而不关心同一个对象的代码完全则可以闯入、执行到lock内部。
------解决思路----------------------
引用:
Quote: 引用:

明明是两个不同的锁,谁说是一个了

老师有没有教过你看书要认真,看行不漏字。。。被lock锁住的代码还有什么必要再嵌套一层lock呢

应该说,lock(a)里没必要再放lock(a)
但是有时候确实需要lock(a)里再嵌套lock(b)

比如a,b两个线程互斥
c,d两个线程互斥
然后它们还都访问一个公共的list
------解决思路----------------------
(ICollection)MainUrls).SyncRoot

引用:
http://kidneyball.iteye.com/blog/850053
附结论:继续想了一下,似乎在这里使用DoubleCheck方式的主要出发点是”避免同步锁自身的初始化开销”,而不是”避免被锁住内容的执行开销“。那么问题就变为”同步锁自身的初始化开销,是否足以抵消额外的条件判断的性能损耗“。看来还是要实测一下。


你看错了吧,他那分明是DoubleLock,完全没有必要的搞法
人家的想法可能是像3#猜想,主要是为了防止在其他线程里,在没有lock()的情况下修改了集合从而导致出错
事实上,只要保证读写集合的地方都lock这个对象就不会发生这种意外了
如果你检查了已有的代码,并且保证新增的代码都遵守上面这条,就完全可以去掉内层的lock

如果你用的是Net3.0以上,你完全可以用封装好的System.Collections.Generic.SynchronizedCollection<T>
Net4.0之后还增加了System.Collections.Concurrent.ConcurrentQueue<T>等各种线程安全的集合,用起来就跟普通的集合一样,只是内部帮你做了线程同步处理
  相关解决方案