Question

J'ai cet objet de cache síngleton et il expose une propriété IEnumerable qui ne renvoie qu'une variable privée IEnumerable.

J'ai une méthode statique sur mon objet singleton qui met à jour cette variable membre (qui existe sur la seule instance 'Instance' de cet objet cache).

Supposons qu'un fil itère actuellement sur cette variable / propriété IEnumerable pendant la mise à jour de mon cache. Je l'ai fait pour que le cache se mette à jour sur une nouvelle variable locale et pour finir en définissant la variable privée exposée pour qu'elle pointe vers cette nouvelle variable locale.

Je sais que je suis en train de mettre à jour une référence, laissant l'autre (ancien) objet en mémoire en attente d'être récupéré par le GC, mais mon problème est que - je ne suis pas sûr à 100% de ce qui se passera une fois la nouvelle référence définie. ? Est-ce que l'autre fil se retrouverait soudainement sur le nouvel objet ou sur l'ancien objet passé à travers l'interface IEnumerable? Si cela avait été une référence normale, je dirais «non». Le thread appelant fonctionnerait sur l'ancien objet, mais je ne suis pas sûr qu'il en soit ainsi pour IEnumerable?

Voici la classe dépouillée:

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
    }
}
Était-ce utile?

La solution

Le thread qui énumère actuellement sektioner continuera de l'énumérer même lorsque vous mettez à jour la référence dans le singleton. Les objets qui implémentent IEnumerable n’ont rien de spécial.

Vous devriez peut-être ajouter le volatile mot-clé du champ sektioner, car vous ne fournissez pas de verrouillage en lecture et plusieurs threads le lisent / écrivent.

Autres conseils

Depuis le getter {return sektioner; } est appelé avant que la nouvelle valeur ne soit placée dans le champ, l'ancienne valeur est renvoyée. Ensuite, la boucle foreach (la séquence est dans cache.Sektioner) utilise la valeur reçue lors de l’appel du getter, c’est-à-dire l’ancienne valeur. Cette valeur sera utilisée tout au long de la boucle foreach.

Tout d'abord, je ne vois pas le verrouillage d'objet, la variable lockObject non utilisée me rend triste. IEnumerable n'est pas spécial. Chaque thread aura sa propre copie de référence à une instance d'objet sektioner. Vous ne pouvez pas affecter d'autres discussions de cette façon. Ce qui arriverait avec l’ancienne version des données pointées par le champ sektioner dépend en grande partie de l’appelant.

Je pense que si vous voulez un fil de sécurité, vous devriez utiliser cette façon:

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
    //}
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top