Domanda

Ho ottenuto questo oggetto cache ngleton ed espone una proprietà IEnumerable che restituisce solo una variabile IEnumerable privata.

Ho un metodo statico sul mio oggetto singleton che aggiorna questa variabile membro (che esiste sulla singola istanza "Instance" di questo oggetto cache).

Supponiamo che alcuni thread stiano attualmente ripetendo su questa variabile / proprietà IEnumerable mentre la mia cache si sta aggiornando. L'ho fatto in modo che la cache si stia aggiornando su una nuova variabile locale e infine impostando la variabile privata esposta in modo che punti a questa nuova variabile locale.

So che sto solo aggiornando un riferimento, lasciando l'altro (vecchio) oggetto in memoria in attesa di essere raccolto dal GC ma il mio problema è: non sono sicuro al 100% di cosa succede una volta impostato il nuovo riferimento ? L'altro thread avrebbe improvvisamente ripetuto il nuovo oggetto o quello vecchio che è stato passato attraverso l'interfaccia IEnumerable? Se fosse stato un riferimento normale direi "no". Il thread chiamante funzionerebbe sul vecchio oggetto, ma non sono sicuro che sia così anche per IEnumerable?

Ecco la classe ridotta:

internal sealed class SektionCache : CacheBase
{
    public static readonly SektionCache Instance = new SektionCache();
    private static readonly object lockObject = new object();
    private static bool isUpdating;

    private IEnumerable<Sektion> sektioner;

    static SektionCache()
    {
        UpdateCache();
    }

    public IEnumerable<Sektion> Sektioner
    {
        get { return sektioner; }
    }

    public static void UpdateCache()
    {
    // SNIP - getting data, locking etc.
    Instance.sektioner = newSektioner;
    // SNIP
    }
}
È stato utile?

Soluzione

Il thread che sta attualmente enumerando sektioner continuerà a enumerarlo anche quando si aggiorna il riferimento all'interno del singleton. Non c'è niente di speciale negli oggetti che implementano IEnumerable.

Dovresti forse aggiungere volatile parola chiave nel campo sektioner poiché non stai fornendo il blocco della lettura e più thread lo stanno leggendo / scrivendo.

Altri suggerimenti

Poiché il getter {restituisce sektioner; } viene chiamato prima che il nuovo valore venga inserito nel campo, viene restituito il vecchio valore. Quindi, il ciclo foreach (Sektion s in cache.Sektioner) utilizza il valore ricevuto quando è stato chiamato il getter, ovvero il vecchio valore. Tale valore verrà utilizzato in tutto il ciclo foreach.

Prima di tutto non riesco a vedere il blocco degli oggetti, la variabile lockObject non utilizzata mi rende triste. IEnumerable non è speciale. Ogni thread avrà la propria copia di riferimento a qualche istanza dell'oggetto sektioner. Non puoi influenzare altri thread in questo modo. Cosa succederebbe con la vecchia versione dei dati puntata dal campo del sektioner dipende in gran parte dal chiamante.

Penso che, se vuoi una sicurezza thread, dovresti usare in questo modo:

internal sealed class SektionCache : CacheBase
{
    //public static readonly SektionCache Instance = new SektionCache();

    // this template is better ( safer ) than the previous one, for thread-safe singleton patter >>>
    private static SektionCache defaultInstance;
    private static object readonly lockObject = new object();
    public static SektionCach Default {
        get {
            SektionCach result = defaultInstance;
            if ( null == result ) {
                lock( lockObject ) {
                    if ( null == result ) {
                        defaultInstance = result = new SektionCache();
                    }
                }
            }

            return result;
        }
    }
    // <<< this template is better ( safer ) than the previous one

    //private static readonly object lockObject = new object();
    //private static bool isUpdating;
    //private IEnumerable<Sektion> sektioner;

    // this declaration is enough
    private volatile IEnumerable<Sektion> sektioner;

    // no static constructor is required >>>
    //static SektionCache()
    //{
    //    UpdateCache();
    //}
    // <<< no static constructor is required

    // I think, you can use getter and setter for reading & changing a collection
    public IEnumerable<Sektion> Sektioner {
        get {
            IEnumerable<Sektion> result = this.sektioner;
            // i don't know, if you need this functionality >>>
            // if ( null == result ) { result = new Sektion[0]; }
            // <<< i don't know, if you need this functionality
            return result;
        }
        set { this.sektion = value; }
    }

    //public static void UpdateCache()
    //{
    //// SNIP - getting data, locking etc.
    //Instance.sektioner = newSektioner;
    //// SNIP
    //}
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top