如何在C#中刷新单例

时间:2022-01-10 05:20:37

I have singleton that fetches from DB, hence it is expensive load. It is lazy loaded.

我有从DB获取的单例,因此它是昂贵的负载。它是懒惰的。

I would like to create a method that refreshes that singleton and populates it once it is required.

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

the data is DB and very expensive, so I want to refresh it only once in case I have concurrent calls. (that is, if I get 500 calls to refresh, I want to restart the refresh only once)

数据是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;
            }
        }

thanks

谢谢

7 个解决方案

#1


2  

This is slightly incorrect, your

这有点不正确,你的

if (m_Instance == null)

if(m_Instance == null)

should really be on the inside of the lock.

应该真的在锁的内侧。

Sorry, didn't spot that.

对不起,没发现那个。

Nothing is built in that can make other clients abandon calls silently if you are already refreshing. Timeouts will generate an exception I think. Perhaps maintain a stale DateTime that you can check to avoid doing the refresh on queued callers.

如果您已经刷新,那么可以使其他客户端无声地放弃呼叫。我认为超时会产生异常。也许维护一个陈旧的DateTime,您可以检查以避免在排队的调用者上进行刷新。

#2


1  

Aside from the double-checked locking being broken (correction, apparently it does work but I still find it not particularly pretty), if you have write access to m_Instance why not just set it to new PageData() there and then in ReSync?

除了双重检查锁定被破坏(校正,显然它确实有效,但我仍然发现它不是特别漂亮),如果你有m_Instance的写访问权限,为什么不在那里将其设置为新的PageData()然后在ReSync中?

#3


1  

In my understanding this should work.

根据我的理解,这应该有效。

Here is my code:

这是我的代码:

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.
                    }
                }
        }

#4


0  

How about if you include a "lastRefreshed" member which you also check and lock while refreshing. So if the last refresh occurred within X time, it doesn't happen again?

如果你包括一个“lastRefreshed”成员,你也会在刷新时检查并锁定。因此,如果最后一次刷新发生在X时间内,它不会再发生?

#5


0  

As I understand, you expect concurrent calls to Resync method? This really shouldn't happen if you call it only once in 500 requests from clients. Nevertheless, maybe then it's better idea not to just lock on instanceLock, because then singleton would be reinstantiated multiple times anyway, only not at 'same' time.

据我所知,您期望并发调用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


0  

If you want it to comply with ECMA memory model, I guess the implementation should be something like this (assuming m_Instance is not volatile):

如果你希望它符合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;
}

If you have defined m_Instance to be volatile there is only one main difference. The m_Instance needs to be read to local variable before the null check is made, because the ReSync() method may set the shared variable to null. I also removed the lock from ReSync() as it is not really needed. The race to initialize a new instance is safe.

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

#7


0  

I may need to understand better how the concurrancy came into this. Your description seems to fit into the scope of System.Cashing namespace. You tell that the object should store its information for an amount of time (some options are available there).

我可能需要更好地了解这种同情心是如何形成的。您的描述似乎符合System.Cashing命名空间的范围。你告诉对象应该存储一段时间的信息(那里有一些选项)。

If the next request are same as the previous cashed (you define the criteria of 'identic' yourself) the caller will get the cached copy instead. In real, its means no new db connection. Just a memory request. If the valid cache criteria has passed i.e 30 minutes) the next request are against the database (for another amount of time).

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

Requires memory but are extremely fast and recommended in scenarios where data are reused.

需要内存但速度非常快,建议在重复使用数据的情况下使用。

#1


2  

This is slightly incorrect, your

这有点不正确,你的

if (m_Instance == null)

if(m_Instance == null)

should really be on the inside of the lock.

应该真的在锁的内侧。

Sorry, didn't spot that.

对不起,没发现那个。

Nothing is built in that can make other clients abandon calls silently if you are already refreshing. Timeouts will generate an exception I think. Perhaps maintain a stale DateTime that you can check to avoid doing the refresh on queued callers.

如果您已经刷新,那么可以使其他客户端无声地放弃呼叫。我认为超时会产生异常。也许维护一个陈旧的DateTime,您可以检查以避免在排队的调用者上进行刷新。

#2


1  

Aside from the double-checked locking being broken (correction, apparently it does work but I still find it not particularly pretty), if you have write access to m_Instance why not just set it to new PageData() there and then in ReSync?

除了双重检查锁定被破坏(校正,显然它确实有效,但我仍然发现它不是特别漂亮),如果你有m_Instance的写访问权限,为什么不在那里将其设置为新的PageData()然后在ReSync中?

#3


1  

In my understanding this should work.

根据我的理解,这应该有效。

Here is my code:

这是我的代码:

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.
                    }
                }
        }

#4


0  

How about if you include a "lastRefreshed" member which you also check and lock while refreshing. So if the last refresh occurred within X time, it doesn't happen again?

如果你包括一个“lastRefreshed”成员,你也会在刷新时检查并锁定。因此,如果最后一次刷新发生在X时间内,它不会再发生?

#5


0  

As I understand, you expect concurrent calls to Resync method? This really shouldn't happen if you call it only once in 500 requests from clients. Nevertheless, maybe then it's better idea not to just lock on instanceLock, because then singleton would be reinstantiated multiple times anyway, only not at 'same' time.

据我所知,您期望并发调用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


0  

If you want it to comply with ECMA memory model, I guess the implementation should be something like this (assuming m_Instance is not volatile):

如果你希望它符合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;
}

If you have defined m_Instance to be volatile there is only one main difference. The m_Instance needs to be read to local variable before the null check is made, because the ReSync() method may set the shared variable to null. I also removed the lock from ReSync() as it is not really needed. The race to initialize a new instance is safe.

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

#7


0  

I may need to understand better how the concurrancy came into this. Your description seems to fit into the scope of System.Cashing namespace. You tell that the object should store its information for an amount of time (some options are available there).

我可能需要更好地了解这种同情心是如何形成的。您的描述似乎符合System.Cashing命名空间的范围。你告诉对象应该存储一段时间的信息(那里有一些选项)。

If the next request are same as the previous cashed (you define the criteria of 'identic' yourself) the caller will get the cached copy instead. In real, its means no new db connection. Just a memory request. If the valid cache criteria has passed i.e 30 minutes) the next request are against the database (for another amount of time).

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

Requires memory but are extremely fast and recommended in scenarios where data are reused.

需要内存但速度非常快,建议在重复使用数据的情况下使用。