2011-03-16 20 views
5

Ho singleton che recupera da DB, quindi è carico costoso. È carico pigro.Come aggiornare singleton in C#

Vorrei creare un metodo che aggiorna quel singleton e lo popola quando è necessario.

i dati sono DB e molto costosi, quindi voglio aggiornarlo solo una volta nel caso in cui abbia chiamate contemporanee. (Cioè, se ottengo 500 chiamate per aggiornare, voglio riavviare l'aggiornamento solo una volta)

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

grazie

+0

Come deve decidere il sistema quando è necessario un aggiornamento? C'è un intervallo durante il quale tutte le chiamate devono essere considerate come la stessa? –

risposta

2

Questo è leggermente corretto, la vostra

if (m_Instance == null)

dovrebbe davvero essere all'interno della serratura.

Scusate, non l'ho notato.

Nulla è creato in modo che altri client possano abbandonare le chiamate in modo silenzioso se si sta già aggiornando. I timeout genereranno un'eccezione, penso. Forse mantenere un DateTime obsoleto che è possibile verificare per evitare di eseguire l'aggiornamento sui chiamanti in coda.

+0

cosa intendi? questo è il doppio controllo che blocca – Himberjack

+0

m_Instance == null potrebbe essere vero quando lo si controlla, ma falso una volta arrivato al blocco. Devi prima bloccarlo per essere sicuro di essere l'unico ad agire su m_Instance. –

+0

questo è un design standard a doppio controllo di chiusura! – Himberjack

1

A parte il double-checked locking essere rotto (correzione, a quanto pare it does work ma trovo ancora non particolarmente bella), se si ha accesso in scrittura m_Instance perché non solo impostarlo su nuove PageData() lì e poi in risincronizzazione?

+0

Preferisco usare la proprietà incapsulata init – Himberjack

+0

I Ho visto questo menzionato più volte, e di solito collegandomi a una pagina come i link Massif qui, che si lamenta di Java. Questo post non riguarda Java, però. Non ho visto alcuna prova che il blocco del doppio controllo non funzioni in .NET. Sarei interessato a vederlo, se esiste. –

+0

@Gabe, l'implementazione del blocco a doppio controllo va bene, è solo che l'intero schema è rotto, come per il collegamento. (ulteriori ricerche scoprono che funziona ora, ma probabilmente è da evitare: http://stackoverflow.com/questions/394898/double-checked-locking-in-net) – Massif

0

E se includessi un membro "lastRefreshed" che controlli e blocchi durante l'aggiornamento. Quindi se l'ultimo aggiornamento si è verificato entro X volta, non si ripresenta?

0

Se si vuole rispettare modello di memoria ECMA, immagino l'attuazione dovrebbe essere qualcosa di simile (supponendo m_Instance non è 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; 
} 

Se avete definito m_Instance ad essere volatili c'è solo una differenza principale. È necessario leggere m_Instance sulla variabile locale prima che venga eseguito il controllo Null, in quanto il metodo ReSync() può impostare la variabile condivisa su null. Ho anche rimosso il blocco da ReSync() in quanto non è davvero necessario. La corsa per inizializzare una nuova istanza è sicura.

0

Come ho capito, ci si aspetta chiamate contemporanee al metodo di risincronizzazione? Questo in realtà non dovrebbe accadere se lo chiami solo una volta su 500 richieste da parte dei clienti. Ciononostante, forse è meglio non bloccare semplicemente instanceLock, perché in tal caso il singleton verrebbe comunque ripetuto più volte, solo non allo stesso tempo.

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

Nella mia comprensione questo dovrebbe funzionare.

Ecco il mio codice:

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

posso avere bisogno di capire meglio come il concurrancy venuto in questo. La tua descrizione sembra adattarsi allo scopo dello spazio dei nomi System.Cashing. Si dice che l'oggetto dovrebbe memorizzare le sue informazioni per un certo periodo di tempo (alcune opzioni sono disponibili qui).

Se la richiesta successiva è uguale alla precedente incassata (si definiscono i criteri di 'identico'), il chiamante riceverà invece la copia memorizzata nella cache. In realtà, significa che non c'è una nuova connessione db. Solo una richiesta di memoria. Se i criteri di cache validi sono passati i.e 30 minuti) la richiesta successiva è contro il database (per un altro periodo di tempo).

Richiede memoria ma è estremamente veloce e consigliato in scenari in cui i dati vengono riutilizzati.

+0

In termini di aggiornamento. Ogni singola cache può essere resettata in qualsiasi momento prescelto con un metodo .Clear() – Independent