为什么魔术会锁定System.Object的实例而不是锁定特定的实例类型?

pgh*_*ech 9 c# multithreading locking

我一直在学习锁定线程,我还没有找到解释为什么创建一个典型的System.Object,锁定它并执行锁定期间所需的任何操作提供线程安全?

object obj = new object()
lock (obj) {
  //code here
}
Run Code Online (Sandbox Code Playgroud)

起初我以为它只是被用作示例中的占位符,并且意味着要与您正在处理的类型进行交换.但我发现Dennis Phillips指出的例子似乎与实际使用Object的实例没什么不同.

因此,举一个需要更新私有字典的例子,锁定System.Object的实例做什么来提供线程安全而不是实际锁定字典(我知道在这种情况下锁定字典可能会出现案例同步问题)?如果字典是公开的怎么办?

//what if this was public?
private Dictionary<string, string> someDict = new Dictionary<string, string>();

var obj = new Object();
lock (obj) {
  //do something with the dictionary
}
Run Code Online (Sandbox Code Playgroud)

Jar*_*Par 9

lock本身对该Dictionary<TKey, TValue>类型没有任何安全性.lock基本上是做什么的

对于每个使用lock(objInstance)只有一个线程将在lock给定对象的语句的主体中(objInstance)

如果给定Dictionary<TKey, TValue>实例的每次使用都发生在lock.并且每个人lock使用相同的对象,然后您知道一次只有一个线程访问/修改字典.这对于防止多个线程同时读取和写入并破坏其内部状态至关重要.

但是这种方法存在一个巨大的问题:你必须确保每个字典的使用都发生在一个锁中并且它使用相同的对象.如果你甚至忘记了一个,那么你已经创建了一个潜在的竞争条件,那么就没有编译器警告,并且这个bug可能会在一段时间内未被发现.

在第二个示例中,您显示您正在使用本地对象实例(var表示本地方法)作为对象字段的锁定参数.这几乎肯定是错误的.本地人只会在方法的生命周期内生活.因此,对方法的2次调用将在不同的本地使用锁,因此所有方法都能够同时进入锁.


Hen*_*man 8

过去常见的做法是锁定共享数据本身:

private Dictionary<string, string> someDict = new Dictionary<string, string>();

lock (someDict ) 
{
  //do something with the dictionary
}
Run Code Online (Sandbox Code Playgroud)

但是(有些理论上的)反对意见是,除了你的控制之外的其他代码也可以锁定someDict,然后你可能会遇到死锁.

因此,建议使用一个(非常)私人对象,在1对1的对应的数据宣布,作为替身锁使用.只要访问字典的所有代码都锁定obj了踏板安全性,就可以保证.

// the following 2 lines belong together!!
private Dictionary<string, string> someDict = new Dictionary<string, string>();
private object obj = new Object();


// multiple code segments like this
lock (obj) 
{
  //do something with the dictionary
}
Run Code Online (Sandbox Code Playgroud)

因此,目的obj是充当字典的代理,因为它的类型无关紧要,我们使用最简单的类型,System.Object.

如果字典是公开的怎么办?

然后所有的赌注都关闭,任何代码都可以访问字典,并且包含类之外的代码甚至无法锁定保护对象.在您开始寻找修复之前,这根本不是一个可持续的模式.使用ConcurrentDictionary或保持正常的私有.