何时可以锁定我使用的对象,何时使用专用的synclock对象

时间:2014-05-06 17:23:01

标签: c# multithreading locking

让我们说我有三个不同的类,每个类以稍微不同的方式锁定一个集合。 A类直接锁定集合,B类锁定集合的SyncRoot属性,C类使用专用对象锁定集合。我的理解是,惯例是使用最后两种方法中的一种,但我很好奇它何时可以接受使用A类方法所使用的以及是否存在任何潜在的危险。 B类和C类使用的方法之间是否有任何好处/危险?

public class A
{
    private List<object> _list = new List<object>();

    public void DoStuff()
    {
        lock(_list)
        {
            ...
            ...
        }
    }
}

public class B
{
    private List<object> _list = new List<object();

    public void DoStuff()
    {
        lock(((ICollection)_list).SyncRoot)
        {
            ...
            ...
        }
    }
}

public class C
{
    private List<object> _list = new List<object>();
    private object _listSyncLock = new object();

    public void DoStuff()
    {
        lock(_listSyncLock)
        {
            ...
            ...
        }
    }
}

2 个答案:

答案 0 :(得分:3)

创建私有object以锁定的想法是确保您的类型和仅类型锁定在该对象上。如果你锁定了其他类中可用的某个对象,那么你需要考虑那些其他类正在对该对象做什么,以及当它们锁定它时,当试图推断代码执行时可能发生的事情时。如果您锁定了诸如this或公共属性/字段之类的内容,那么您需要考虑整个应用程序中任何位置的任何代码都可能锁定该对象的可能性。这使得实际上很难对代码进行推理,以避免死锁等等。

这里的问题是列表(据您所示)从未暴露在此类之外。如果列表真的从未暴露在其他地方,那么没有别的东西可以锁定它。如果没有其他任何东西可以锁定它,那么锁定就没有问题,也没有真正的理由来创建第二个object来锁定。

SyncLock的目标是明确允许除了列表之外没有共享对象的类型锁定该对象。如果你从未在其他地方公开过列表,那么其他任何东西都无法获取同步锁,所以它与直接锁定列表并没有什么不同。

当然,如果你从外部公开列表(即使你将它转换为IEnumerable之类的东西,因为那个窗台会导致相同的对象引用锁定)然后选项A和B几乎相同,都应该避免。如果您的类型没有要锁定的不变的实例字段,或者它们被公开暴露,那么该模式的整个目的是要锁定某些东西。虽然在某些情况下它只是为了可读性而完成,因为人们已经习惯了这种模式,而且 尽管如上所述,实际上可能没有必要。

答案 1 :(得分:1)

SyncRoot的目的是允许IList<T>的实现可能是某个其他对象的包装器。给出:

var list = new List<String>();
var wrapper = new ReadOnlyCollection<String>(list);
IList ilist1 = list, ilist2 = wrapper;

重要的是,在对ilist2进行更改时,不会尝试从ilist1读取项目。因此,两者必须具有相同的SyncRoot。为此,SyncRoot ReadOnlyCollection<T>属性返回包装集合的SyncRoot。请注意,List<T> syncroot属性返回仅为此目的创建的对象。如果没有,则包含ReadOnlyCollection<String>的{​​{1}}代码可以将其List<String>对象转换为SyncRoot并使用它来修改列表。

唯一的时间List<String>真正相关的是当一些(尽管不是全部)对集合的引用将通过包装器进行时,使用该集合的代码片段并不是都知道彼此的存在。请注意SyncRoot已被大量弃用,因为没有出现一致的使用模式,并且使用这种功能不一致的情况比没有使用它更糟糕。