解释代码:c#锁定功能和线程
我在一些项目中使用了这个模式(这段代码是从中剪下来的),我理解它的作用,但我对这个模式的解释非常感兴趣。具体而言:解释代码:c#锁定功能和线程,c#,multithreading,locking,C#,Multithreading,Locking,我在一些项目中使用了这个模式(这段代码是从中剪下来的),我理解它的作用,但我对这个模式的解释非常感兴趣。具体而言: 为什么重复检查\u dependenciesregisted 为什么要使用lock(lock){} 谢谢 public class DependencyRegistrarModule : IHttpModule { private static bool _dependenciesRegistered; private static readonly object
\u dependenciesregisted
lock(lock){}
public class DependencyRegistrarModule : IHttpModule
{
private static bool _dependenciesRegistered;
private static readonly object Lock = new object();
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
public void Dispose() { }
private static void context_BeginRequest(object sender, EventArgs e)
{
EnsureDependenciesRegistered();
}
private static void EnsureDependenciesRegistered()
{
if (!_dependenciesRegistered)
{
lock (Lock)
{
if (!_dependenciesRegistered)
{
new DependencyRegistrar().ConfigureOnStartup();
_dependenciesRegistered = true;
}
}
}
}
}
这是最新的
lock
语句确保块内的代码不会同时在两个线程上运行。由于
lock
语句有点昂贵,因此代码会在进入锁之前检查它是否已经初始化。但是,因为不同的线程可能在外部检查之后初始化了它,所以它需要在锁内部再次检查
.双重检查是因为两个线程可能同时命中
EnsureDependencesRegistered
,两个线程都发现它未注册,因此都试图获取锁
锁(lock)
本质上是互斥的一种形式;只有一个线程可以拥有锁-另一个线程必须等待锁被释放(在lock(…){…}
语句末尾)
因此在这种情况下,一个线程可能(虽然不太可能)是
锁中的第二个线程,因此每个线程都必须进行双重检查,以防它是第二个线程,并且工作已经完成。这是一个性能问题
如果工作已经完成,那么初始测试可以让它快速摆脱困境。此时,它会执行可能非常昂贵的锁定,但它必须再次检查,因为另一个线程可能已经注册了它。双重检查的锁定模式大致如下:
您有一个要有条件地执行一次的操作
if (needsToDoSomething) {
DoSomething();
needsToDoSomething = false;
}
但是,如果您在两个线程上运行,则两个线程都可能在将标志设置为false之前检查标志并执行操作。因此,您添加了一个锁
lock (Lock) {
if (needsToDoSomething) {
DoSomething();
needsToDoSomething = false;
}
}
然而,每次运行这段代码时获取锁可能会很慢,因此您决定,让我们只在实际需要时尝试获取锁
if (needsToDoSomething)
lock (Lock) {
if (needsToDoSomething) {
DoSomething();
needsToDoSomething = false;
}
}
您无法删除内部检查,因为您再次遇到一个问题,即在锁之外执行的任何检查都可能在两个不同线程上两次为真。该锁阻止两个线程运行ConfigureOnStartup()。在if(!\u dependenciesRegistered)和ConfigureOnStartup()设置\u dependenciesRegistered=true的点之间,另一个线程可以检查它是否已注册。换言之:
线程1:_dependenciesRegistered==false
线程2:_dependenciesRegistered==false
线程1:ConfigureOnStartup()/_dependenciesRegistered=true李>
线程2:没有“看到”它已经注册,所以再次运行ConfigureOnStartup()
+1特别是wikipedia文章中的“这不是最好的方法”注释:当在某些语言/硬件组合中实现该模式时,它可能是不安全的。这不是实现单例的最佳方法,因为很可能会出错。我认为这个实现是有缺陷的,因为它缺少变量上的volatile。在某些情况下,正确实现此模式可能是更好的选择。“这不是最好的方式”链接实际上似乎并不适用,因为它反对此模式的理由是“不能保证在设置变量之前完成单例构造函数”。但是,这里没有将变量设置为构造函数,因此,双重检查锁工作正常,即使没有volatile
@BlueRaja:这种方式仍然比静态构造函数慢。