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? 或者我有线程?

您有一个对象层次结构:

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

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

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

C# lock语句使用Windows 关键部分 ,它是一种轻量级锁定机制。 如果要锁定进程,可以使用互斥锁 。 要锁定服务器,您可以使用数据库或共享文件。

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

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

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

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

lock(表达式)语句块

哪里:

expression指定要锁定的对象。 表达式必须是引用类型。 通常,如果要保护实例变量,则表达式将为此;如果要保护静态变量(或者如果临界区出现在给定类中的静态方法中),则表达式将为typeof(类)。

statement block关键部分的陈述。

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

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

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

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

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

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

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

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