Обновление ссылки на используемую переменную-член

StackOverflow https://stackoverflow.com/questions/114179

  •  02-07-2019
  •  | 
  •  

Вопрос

Я получил этот объект кэша síngleton, и он предоставляет свойство IEnumerable, которое просто возвращает закрытую переменную IEnumerable.

У меня есть статический метод в моем одноэлементном объекте, который обновляет эту переменную-член (которая существует в единственном экземпляре 'Instance' этого объекта кэша).

Допустим, какой-то поток в данный момент выполняет итерацию по этой переменной / свойству IEnumerable, пока мой кэш обновляется.Я сделал это так, чтобы кэш обновлялся по новой локальной переменной и, наконец, устанавливал открытую закрытую переменную так, чтобы она указывала на эту новую локальную переменную.

Я знаю, что я просто обновляю ссылку, оставляя другой (старый) объект в памяти, ожидающий получения GC, но моя проблема в том, что я не уверен на 100%, что произойдет, как только я установлю новую ссылку?Будет ли другой поток внезапно выполнять итерацию по новому объекту или старому, который он передал через интерфейс IEnumerable?Если бы это была обычная ссылка, я бы сказал "нет".Вызывающий поток будет работать со старым объектом, но я не уверен, так ли это и для IEnumerable?

Вот класс в урезанном виде:

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
    }
}
Это было полезно?

Решение

Поток, который в данный момент перечисляет sektioner, продолжит перечислять его, даже когда вы обновите ссылку внутри singleton.Нет ничего особенного в объектах, которые реализуют IEnumerable.

Возможно, вам следует добавить следующее изменчивый ключевое слово в поле sektioner, поскольку вы не предоставляете блокировку чтения, и несколько потоков читают / записывают его.

Другие советы

С момента получения { return sektioner; } вызывается перед вводом в поле нового значения, возвращается старое значение.Затем цикл foreach (Sektion s in cache.Sektioner) использует значение, которое было получено при вызове средства получения, т.е.старое значение.Это значение будет использоваться на протяжении всего цикла foreach.

Во-первых, я не вижу блокировки объекта, неиспользуемая переменная lockObject меня огорчает.IEnumerable не является чем-то особенным.Каждый поток будет иметь свою собственную копию ссылки на некоторый экземпляр объекта sektioner.Вы не можете таким образом влиять на другие потоки.Что произойдет со старой версией данных, указанных в поле sektioner, во многом зависит от вызывающей стороны.

Я думаю, если вы хотите обеспечить потокобезопасность, вам следует использовать этот способ:

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
    //}
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top