【问题标题】:How to refresh singleton in C#如何在 C# 中刷新单例
【发布时间】:2011-03-16 13:37:05
【问题描述】:

我有从 DB 获取的单例,因此负载很昂贵。 它是延迟加载的。

我想创建一个方法来刷新该单例并在需要时填充它。

数据是数据库并且非常昂贵,所以我只想刷新一次,以防我有并发调用。 (也就是说,如果我收到 500 次刷新调用,我只想重新开始刷新一次)

public static PageData Instance
    {
        get
        {
            if (m_Instance == null)
            {
                lock (instanceLock)
                {
                    if (m_Instance == null)
                    {
                        m_Instance = new PageData();
                    }
                }
            }
            return m_Instance;
        }
    }


public void ReSync()
        {                         
            lock (instanceLock)
            {
                /* Setting to null to force the Instance to re-build */
                m_Instance = null;
                PageData pData = Instance;
            }
        }

谢谢

【问题讨论】:

  • 系统应该如何决定何时需要刷新?是否有一个时间跨度,在此期间所有呼叫都应视为同一个呼叫?

标签: c# asp.net singleton


【解决方案1】:

这有点不正确,你的

if (m_Instance == null)

应该真的在锁的内侧。

抱歉,没发现。

如果您已经在刷新,没有任何内置功能可以让其他客户端静默放弃呼叫。我认为超时会产生异常。也许维护一个陈旧的 DateTime,您可以检查它以避免对排队的调用者进行刷新。

【讨论】:

  • 什么意思?这是双重检查锁定
  • m_Instance == null 在您检查时可能为真,但在您锁定时为假。您需要先锁定它,以确保您是唯一一个作用于 m_Instance 的人。
  • 这是标准的双重检查锁定设计!
  • 再看一遍——他检查了两次(一次在外面,然后又在锁里)。
  • @Adam - 他正在正确使用双重检查锁定。他在获取锁之前和之后检查它是否为空。
【解决方案2】:

据我了解,这应该可行。

这是我的代码:

private static instanceLock = new object();
private static _refreshing = true;

public static PageData Instance
    {
        get
        {
            if (_refreshing)
            {
                lock (instanceLock)
                {
                    if (_refreshing)
                    {
                        m_Instance = new PageData();
                        _refreshing = false; //now allow next refreshes.
                    }
                }
            }
            return m_Instance;
        }
    }


public void ReSync()
        {
            if (!_refreshing)                         
                lock (instanceLock)
                {
                    if (!_refreshing)
                    {
                        _refreshing = true; //don't allow refresh until singleton is called.
                    }
                }
        }

【讨论】:

    【解决方案3】:

    除了 double-checked locking 被破坏(更正,显然是 it does work 但我仍然觉得它不是特别漂亮),如果你对 m_Instance 有写访问权,为什么不把它设置为 new PageData() 那里然后在 ReSync ?

    【讨论】:

    • 我更喜欢使用封装的属性init
    • 我已经多次看到这一点,并且通常链接到诸如 Massif 链接之类的页面,该页面抱怨 Java。不过,这篇文章不是关于 Java 的。我没有看到任何真正的证据表明双重检查锁定在 .NET 中不起作用。如果它存在的话,我很想看看它。
    • @Gabe,双重检查锁定的实现很好,只是整个模式被打破了,根据链接。 (进一步研究发现它现在确实有效,但可能需要避免:stackoverflow.com/questions/394898/…
    • @mgronber:所以它没有损坏,只是缺少volatile
    • @Gabe:好吧,如果volatile 丢失,它就坏了。我们不知道真相,因为例子没有定义。但是,volatile 仅修复了新实例的初始化。由于m_Instance 被读取了两次,因此代码仍然被破坏。首先读取它以检查它是否为非空,然后在返回时再次读取它。对ReSync() 的调用可能会在这两次读取之间将其设置为空。这个错误甚至不需要 IA64。
    【解决方案4】:

    如果您包含一个“lastRefreshed”成员,您在刷新时也检查并锁定该成员怎么样。那么如果最后一次刷新发生在 X 时间内,就不会再次发生?

    【讨论】:

      【解决方案5】:

      据我了解,您希望并发调用 Resync 方法?如果您在来自客户的 500 个请求中只调用一次,这真的不应该发生。尽管如此,也许最好不要只锁定instanceLock,因为那样单例无论如何都会被重新实例化多次,只是不会在“同一”时间。

      public void ReSync()
      {
        if (!m_IsResyncing)
        {
          lock (m_resyncLock)
          {
            if (!m_IsResyncing)
            {
              m_IsResyncing = true;
              Thread.Sleep(100); // sleep for 100ms to accumulate other locks
              // reinitialize here
              m_IsResyncing = false;
            }
          }
        }
      }
      

      【讨论】:

        【解决方案6】:

        如果你希望它符合 ECMA 内存模型,我想实现应该是这样的(假设 m_Instance 不是易失性的):

        public static PageData Instance
        {
            get
            {
                PageData instance = Thread.VolatileRead(ref m_Instance);
                if (instance == null)
                {
                    lock (instanceLock)
                    {
                        instance = Thread.VolatileRead(ref m_Instance);
                        if (instance == null)
                        {
                            instance = new PageData();
                            Thread.VolatileWrite(ref m_Instance, instance);
                        }
                    }
                }
        
                return instance;
            }
        }
        
        public void ReSync()
        {
            /* Setting to null to force the Instance to re-build */
            Thread.VolatileWrite(ref m_Instance, null);
            PageData pData = Instance;
        }
        

        如果您将m_Instance 定义为易失性,则只有一个主要区别。在进行空检查之前,需要将m_Instance 读取到局部变量,因为ReSync() 方法可能会将共享变量设置为空。我还从ReSync() 中删除了锁,因为它并不是真正需要的。初始化新实例的竞赛是安全的。

        【讨论】:

          【解决方案7】:

          我可能需要更好地了解并发性是如何产生的。您的描述似乎适合 System.Cashing 命名空间的范围。您告诉对象应该将其信息存储一段时间(那里有一些选项可用)。

          如果下一个请求与上一个兑现的请求相同(您自己定义“相同”的标准),则调用者将获得缓存的副本。实际上,这意味着没有新的数据库连接。只是一个内存请求。如果有效的缓存条件已经过去,即 30 分钟),则下一个请求将针对数据库(再持续一段时间)。

          需要内存,但速度极快,建议在重复使用数据的场景中使用。

          【讨论】:

          • 刷新方面。可以使用 .Clear() 方法在任何选定的时间重置每个单独的缓存
          猜你喜欢
          • 2020-02-18
          • 1970-01-01
          • 1970-01-01
          • 2014-05-03
          • 2013-11-23
          • 1970-01-01
          • 2015-01-08
          • 2010-11-25
          相关资源
          最近更新 更多