【问题标题】:Explain the code: c# locking feature and threads解释代码:c#锁定特性和线程
【发布时间】:2010-04-29 21:10:05
【问题描述】:

我在几个项目中使用了这种模式,(这段代码来自CodeCampServer),我理解它的作用,但我对这种模式的解释非常感兴趣。具体来说:

  1. 为什么_dependenciesRegistered的双重检查。
  2. 为什么要使用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;
                }
            }
        }
    }
}

【问题讨论】:

    标签: c# multithreading locking


    【解决方案1】:

    这是Double-checked locking pattern

    lock 语句确保块内的代码不会同时在两个线程上运行。
    由于lock 语句有点昂贵,代码会在进入锁之前检查它是否已经被初始化。
    但是,由于其他线程可能在外部检查之后对其进行了初始化,因此它需要再次检查锁内部。

    Note that this is not the best way to do it.

    【讨论】:

    • +1 特别是“这不是最好的方法”说明
    • 在维基百科文章中:该模式在某些语言/硬件组合中实现时可能是不安全的。
    • 这不是实现单例的最佳方式,因为你很可能会弄错。因为我相信这个实现是有缺陷的,因为它缺少变量上的 volatile 。这种模式如果实施得当,在某些情况下可能是更好的选择。
    • “这不是最好的方法” 链接实际上似乎并不适用,因为它反对这种模式的论点是 “单例构造函数不是” t 保证在设置变量之前完成。” 但是,这里没有将变量设置为构造函数,因此双重检查锁工作正常,即使没有volatile
    • @BlueRaja:这种方式仍然会比静态构造函数慢。
    【解决方案2】:

    仔细检查是因为两个线程可能同时命中EnsureDependenciesRegistered,都发现它没有注册,因此都试图获取锁。

    lock(Lock)本质上是互斥体的一种形式;只有一个线程可以拥有锁 - 另一个必须等​​到锁被释放(在 lock(...) {...} 语句的末尾)。

    所以在这种情况下,线程可能(虽然不太可能)是lock 中的第二个线程 - 所以每个线程都必须仔细检查以防它是 第二个 em>,工作已经完成了。

    【讨论】:

      【解决方案3】:

      这是性能问题。

      如果工作已经完成,初始测试可以让它快速退出。此时它会执行潜在的昂贵锁,但它必须再次检查它,因为另一个线程可能已经注册了它。

      【讨论】:

        【解决方案4】:

        双重检查锁定模式大致是:

        你有一个操作想要有条件地执行一次

        if (needsToDoSomething) {
           DoSomething();
           needsToDoSomething = false;
        }
        

        但是,如果您在两个线程上运行,则两个线程都可能会在将标志都设置为 false 之前检查标志并执行操作。因此,您添加了一个锁。

        lock (Lock) {
            if (needsToDoSomething) {
               DoSomething();
               needsToDoSomething = false;
            }
        }
        

        但是,每次运行此代码时获取锁可能会很慢,因此您决定,只在我们真正需要时尝试获取锁。

         if (needsToDoSomething)
            lock (Lock) {
                if (needsToDoSomething) {
                   DoSomething();
                   needsToDoSomething = false;
                }
            }
        

        您不能删除内部检查,因为再次遇到的问题是,在锁之外执行的任何检查都可能在两个不同的线程上两次变为真。

        【讨论】:

          【解决方案5】:

          锁阻止两个线程运行 ConfigureOnStartup()。在 if (!_dependenciesRegistered) 和 ConfigureOnStartup() 设置 _dependenciesRegistered = true 之间,另一个线程可以检查它是否已注册。换句话说:

          1. 线程 1:_dependenciesRegistered == false
          2. 线程 2:_dependenciesRegistered == false
          3. 线程 1:ConfigureOnStartup() / _dependenciesRegistered = true;
          4. 线程 2:没有“看到”它已经注册,因此再次运行 ConfigureOnStartup()。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-12-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多