2011-03-16 16 views
5

Mam singleton, który pobiera z DB, a więc jest to kosztowne obciążenie. Jest leniwy załadowany.Jak odświeżyć singleton w C#

Chciałbym stworzyć metodę, która odświeża singleton i zapełnia go, gdy jest to wymagane.

dane są DB i bardzo drogie, więc chcę odświeżyć je tylko raz, w przypadku gdy mam równoczesne połączenia. (To znaczy, jeśli mam 500 połączeń do odświeżania, chcę, aby ponownie uruchomić odświeżanie tylko raz)

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

dzięki

+0

W jaki sposób system powinien zdecydować, kiedy wymagane jest odświeżenie? Czy istnieje czas, w którym wszystkie połączenia powinny być traktowane jako te same? –

Odpowiedz

2

Jest to nieco błędne, twój powinien

if (m_Instance == null)

naprawdę znajduj się wewnątrz zamka.

Niestety, nie zauważyłem tego.

Nic nie jest wbudowane, co może spowodować, że pozostali klienci po cichu porzucą połączenia, jeśli już się odświeżysz. Limity czasu generują wyjątek, który myślę. Być może zachowaj nieaktualny DateTime, który możesz sprawdzić, aby uniknąć odświeżania w kolejce oczekujących.

+0

co masz na myśli? jest to blokada z podwójnym sprawdzeniem – Himberjack

+0

m_Instance == wartość null może być prawdziwa, gdy ją sprawdzasz, ale wartość false, gdy dojdziesz do blokady. Najpierw musisz go zablokować, aby upewnić się, że jesteś jedyną osobą działającą na m_Instance. –

+0

jest to standardowy, podwójny wzór z blokadą! – Himberjack

1

Oprócz double-checked locking łamane (korekta, najwyraźniej it does work ale nadal go nie jest szczególnie ładny znaleźć), jeśli masz dostęp do zapisu m_Instance dlaczego nie wystarczy ustawić go do nowego PageData() tam, a następnie w RESYNC?

+0

Preferuję użycie enkapsulowanej właściwości init – Himberjack

+0

I "Widziałem to wielokrotnie i zwykle link do strony takiej jak linki Massif tutaj, która narzeka na Javę. Ten post nie dotyczy jednak Javy. Nie widziałem żadnych dowodów na to, że podwójne blokowanie nie działa w .NET. Byłbym zainteresowany widząc go, jeśli istnieje. –

+0

@Gabe, implementacja podwójnie sprawdzanego blokowania jest w porządku, po prostu cały szablon jest zepsuty, zgodnie z linkiem. (dalsze badania dowiodły, że teraz działa, ale prawdopodobnie należy ich unikać: http://stackoverflow.com/questions/394898/double-checked-locking-in-net) – Massif

0

Co powiesz na dodanie członka "lastRefreshed", który również sprawdzasz i blokujesz podczas odświeżania. Więc jeśli ostatnie odświeżenie wystąpiło w czasie X, to się nie powtórzy?

0

Jeśli chcesz go do wykonania modelu pamięci ECMA, myślę, że realizacja powinna być mniej więcej tak (zakładając m_Instance nie jest lotny):

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

Jeśli zdefiniowano m_Instance być lotny jest tylko jedna główna różnica. Przed wykonaniem zerowej kontroli należy odczytać lokalną zmienną m_Instance do zmiennej lokalnej, ponieważ metoda ReSync() może ustawić wspólną zmienną na wartość null. Usunąłem także blokadę z ReSync(), ponieważ nie jest to naprawdę potrzebne. Wyścig w celu zainicjowania nowej instancji jest bezpieczny.

0

Jak rozumiem, oczekujesz równoczesnych wywołań metody Resync? To naprawdę nie powinno się zdarzyć, jeśli wywołasz je tylko raz w 500 żądaniach od klientów. Niemniej jednak, może lepiej jest nie zamykać instancji LockLock, ponieważ wtedy i tak wiele razy zostanie przywrócona, ale nie w tym samym czasie.

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

W moim rozumieniu to powinno zadziałać.

Oto mój kod:

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

może trzeba lepiej zrozumieć, w jaki sposób concurrancy przyszedł do tego. Twój opis wydaje się pasować do zakresu przestrzeni nazw System.Cashing. Mówisz, że obiekt powinien przechowywać swoje informacje przez pewien czas (niektóre opcje są tam dostępne).

Jeśli następne żądanie jest takie samo, jak poprzednio zrealizowane (sam zdefiniujesz kryterium "identyczności"), wywołanie otrzyma kopię z pamięci podręcznej. W rzeczywistości oznacza to brak nowego połączenia db. Tylko prośba o pamięć. Jeśli poprawne kryteria bufora podręcznego minęły, tj. 30 minut), następne żądanie dotyczy bazy danych (przez inny czas).

Wymaga pamięci, ale jest wyjątkowo szybki i zalecany w scenariuszach, w których dane są ponownie wykorzystywane.

+0

Pod względem odświeżania. Każdą pojedynczą pamięć podręczną można zresetować w dowolnym wybranym czasie za pomocą metody .Clear() – Independent