开发者

How to refresh singleton in C#

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

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)

public static PageData Instance
    {
        get
        {
            if (m_Instance == null)
            {
                lock (instanceLock)
                {
                    if (m_Instance == null)
                    {
                        m_Instance = new PageData();
                    }
                }
            }
            return m_开发者_Go百科Instance;
        }
    }


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

thanks


This is slightly incorrect, your

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.


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


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?


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?


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.

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


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):

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.


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

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

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜