问题 lock()语句会阻塞进程/ appdomain中的所有线程吗?


也许问题听起来很愚蠢,但我不明白'关于线程和锁定的东西,我想得到一个确认(这就是我问的原因)。

所以,如果我有10个服务器,同时有10个请求来到每个服务器,那就是整个服务器场的100个请求。没有锁定,那就是100请求数据库。

如果我做这样的事情:

private static readonly object myLockHolder = new object();
if (Cache[key] == null)
{
   lock(myLockHolder)
   {
      if (Cache[key] == null)
      {
         Cache[key] = LengthyDatabaseCall();
      }
   }
}

我会做多少个数据库请求? 10? 100?或者我有线程?


5740
2018-06-08 12:58


起源

谢谢大家的优秀解释,我测试了它,它的工作方式与你们所建议的一样--10分贝的请求。 - MikeJ


答案:


您有一个对象层次结构:

  • 你有服务器(10)
  • 在每台服务器上,您都有进程(可能只有1个 - 您的服务/应用程序池)
  • 在每个进程中你都有线程(可能很多)

你的代码会 只要 禁止同一进程内的线程在同一服务器上访问修改 Cache 同时反对。您可以跨进程甚至跨服务器创建锁,但随着层次结构向上移动,成本会增加很多。

使用 lock 声明实际上并没有  任何线程。但是,如果一个线程正在锁内执行代码(即代码块后面的代码块) lock 语句)任何其他想要获取锁并执行相同代码的线程必须等到持有锁的第一个线程离开代码块并释放锁。

C# lock 声明使用Windows 关键部分 其中一个轻量级锁定机制。如果要锁定进程,可以使用 互斥 代替。要锁定服务器,您可以使用数据库或共享文件。

正如dkackman所指出的,.NET具有AppDomain的概念,这是一种轻量级进程。每个进程可以有多个AppDomain。 C# lock 语句仅锁定单个AppDomain中的资源,并且层次结构的正确描述将包括进程下方和线程上方的AppDomain。但是,通常在一个进程中只有一个AppDomain,这种区别在某种程度上是无关紧要的。


10
2018-06-08 13:05



轻微更正“您的代码将仅禁止同一AppDomain中的线程”。 C#静态绑定到AppDomain,而不是进程。 - dkackman


C# lock 语句锁定对象的特定实例(您使用的对象创建的对象) new object())。对象(在大多数情况下)不在AppDomains之间共享,因此如果您有10台服务器,则10个线程可以使用该段代码同时访问您的数据库。


2
2018-06-08 13:02





锁不会阻塞线程。 它锁定了一个对象的实例。尝试访问它的每个线程都被阻止。 因此,在您的情况下,将尝试访问myLockHolder的每个线程都将被锁定,而不是所有线程。 换句话说,我们可以这么说 锁定声明 是使用的语法糖 关键部分

就像你在MSDN中看到的那样:

lock(表达式)语句块

哪里:

表达 指定要锁定的对象。表达必须   是一个参考类型。通常情况下,   表达式将是这个,如果你   想要保护实例变量,   或者typeof(class),如果你愿意的话   保护一个静态变量(如果是   临界区发生在静态   给定类中的方法)。

声明块 关键部分的陈述。


2
2018-06-08 13:52





lock 将阻止该应用程序中的所有线程访问myLockHolder对象。

因此,如果您运行了10个应用程序实例,那么当对象被锁定时,将向服务器发出10个请求。退出lock语句的那一刻,下一个请求将在该应用程序中处理,但只要 Cache[key] 不是 null,它不会访问数据库..

您获得的实际请求数取决于此处发生的情况:

  if (Cache[key] == null)
  {
     Cache[key] = LengthyDatabaseCall();
  }

如果 LengthyDatabaseCall(); 失败,下一个请求将尝试访问数据库服务器并检索信息,所以最好的情况是,只有10个请求到服务器。


1
2018-06-08 12:59





只有在另一个线程正在使用它时需要访问您的共享变量的线程才会进入等待状态。

在任何给定时间有多少是很难确定的。


0
2018-06-08 13:00





您的数据库将获得10个请求,可能性很高,请求2-10运行速度比请求1快得多。


0
2018-06-08 13:10