为什么必须在锁语句中指定同步对象

本文关键字:同步 对象 语句 为什么 | 更新日期: 2023-09-27 17:49:48

我想把我的脑子集中在锁语句中到底发生了什么。

如果我理解正确的话,锁语句是语法糖,下面是…

Object _lock = new Object();
lock (_lock)
{
    // Critical code section
}

…大致翻译成:

Object _lock = new Object();
Monitor.Enter(_lock);
try
{
    // Critical code section
}
finally { Monitor.Exit (_lock); }

我使用了几次lock语句,并且总是创建一个私有字段_lock,作为专用的同步对象。我理解为什么不应该锁定公共变量或类型。

但是为什么编译器不创建那个实例字段呢?我觉得可能在某些情况下,开发人员想要指定要锁定什么,但从我的经验来看,在大多数情况下,这根本不感兴趣,您只是想要锁定!那么为什么没有无参数的锁重载呢?

lock()
{
   // First critical code section
}
lock()
{
   // Second critical code section
}

将被翻译成(或类似的):

[DebuggerHidden]
private readonly object _lock1 = new object()
[DebuggerHidden]
private readonly object _lock2 = new object()
Monitor.Enter(_lock1);
try
{
    // First critical code section
}
finally { Monitor.Exit(_lock1); }
Monitor.Enter(_lock2);
try
{
    // Second critical code section
}
finally { Monitor.Exit(_lock2); }

EDIT:我显然不清楚多锁语句。更新问题,包含两个锁语句

为什么必须在锁语句中指定同步对象

需要存储锁的状态。它是否被输入。这样,另一个试图进入同一锁的线程就可以被阻塞。

需要一个变量。只要一个非常简单的对象,一个普通的对象就足够了。

对于这种变量的一个硬性要求是,它必须在任何锁语句使用它之前创建。尝试像您建议的那样动态创建它会产生一个新问题,现在需要使用锁来安全地创建变量,以便只有进入锁的第一个线程创建它,其他试图进入锁的线程被阻塞,直到它被创建。这需要一个变量。等等,一个无法解决的鸡生蛋还是蛋生鸡的问题。

在某些情况下,您可能需要两个相互独立的不同lock。这意味着当代码的一个"可锁定"部分被锁定时,其他"可锁定"部分不应该被锁定。这就是为什么有能力提供锁对象-您可以为几个独立的lock提供几个锁对象

为了使无变量的东西工作,您必须:

  • 每个锁块有一个自动生成的锁变量(你所做的,这意味着你不能有两个不同的锁块锁定在同一个变量上)
  • 为同一个类中的所有锁块使用相同的锁变量(这意味着你不能有两个独立的东西被保护)

另外,您还需要决定它们应该是实例级的还是静态的。

最后,我猜语言设计者不认为在一个特定情况下的简化值得在阅读代码时引入歧义。线程代码(这是使用锁的原因)已经很难正确编写和验证。让它变得更困难不是一件好事。

允许隐式锁对象可能会鼓励使用单个锁对象,这被认为是不好的做法。通过强制使用显式锁对象,该语言鼓励您将锁命名为有用的名称,例如"countinincementlock"。

这样命名的变量不鼓励开发人员在执行完全独立的操作时使用相同的锁对象,例如写入某种类型的流。

因此,对象可以在一个线程上写入流,而在另一个线程上增加计数器,并且两个线程都不必相互干扰。

语言不这样做的唯一原因是,因为它看起来像一个好的实践,但实际上会隐藏一个坏的实践。

编辑:

也许c#的设计者不想要隐式锁变量,因为他们认为这可能会鼓励不良行为。

也许设计者根本没有想到隐式锁变量,因为他们有其他更重要的事情要先考虑。

如果每个c#开发人员在编写lock()时都确切地知道发生了什么,并且他们知道其含义,那么就没有理由不应该存在,也没有理由不应该按照您建议的方式工作。