简体   繁体   English

更新对正在使用的成员变量的引用

[英]Updating reference to a member variable in use

I got this síngleton cache object and it exposes an IEnumerable property which just returns a private IEnumerable variable.我得到了这个单例缓存对象,它公开了一个 IEnumerable 属性,该属性只返回一个私有的 IEnumerable 变量。

I have a static method on my singleton object that updates this member variable (that exists on the single 'Instance' instance of this cache object).我的单例对象上有一个静态方法,用于更新此成员变量(存在于此缓存对象的单个“实例”实例中)。

Let's say some thread is currently iterating over this IEnumerable variable/property while my cache is updating.假设我的缓存正在更新时,某个线程当前正在迭代这个 IEnumerable 变量/属性。 I made it so the cache is updating on a new local variable and finally setting the exposed private variable to point to this new local variable.我这样做是为了让缓存在一个新的局部变量上更新,最后将公开的私有变量设置为指向这个新的局部变量。

I know i'm just updating a reference, leaving the other (old) object in memory waiting to be picked up by the GC but my problem is - i'm not 100% sure what happens once i set the new reference?我知道我只是在更新一个引用,将另一个(旧)对象留在内存中等待 GC 获取,但我的问题是 - 我不是 100% 确定一旦我设置了新引用会发生什么? Would the other thread suddenly be iterating over the new object or the old one it got passed through the IEnumerable interface?另一个线程会突然迭代新对象还是通过 IEnumerable 接口传递的旧对象? If it had been a normal reference i'd say 'no'.如果它是一个正常的参考,我会说“不”。 The calling thread would be operating on the old object, but i'm not sure if this is the case for IEnumerable as well?调用线程将在旧对象上运行,但我不确定 IEnumerable 是否也是这种情况?

Here is the class stripped down:这是精简的课程:

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

Since the getter { return sektioner; }由于 getter { return sektioner; } { return sektioner; } is called before the new value is put in the field, the old value is returned. { return sektioner; }在将新值放入字段之前调用,返回旧值。 Then, the loop foreach (Sektion s in cache.Sektioner) uses the value that was received when the getter was called, ie the old value.然后, foreach (Sektion s in cache.Sektioner)循环foreach (Sektion s in cache.Sektioner)使用调用 getter 时收到的值,即旧值。 That value will be used throughout the foreach loop.该值将在整个 foreach 循环中使用。

The thread which is currently enumerating sektioner will continue to enumerate it even when you update the reference within the singleton.即使您更新单例中的引用,当前正在枚举 sektioner 的线程也将继续枚举它。 There is nothing special about objects which implement IEnumerable.实现 IEnumerable 的对象没有什么特别之处。

You should perhaps add the volatile keyword to the sektioner field as you are not providing read-locking and multiple threads are reading/writing it.您也许应该将volatile关键字添加到 sektioner 字段,因为您没有提供读锁定并且多个线程正在读/写它。

First of all I can't see object locking, unused lockObject variable makes me sad.首先我看不到对象锁定,未使用的 lockObject 变量让我很伤心。 IEnumerable is not special. IEnumerable 并不特殊。 Each thread will have it's own copy of reference to some instance of sektioner object.每个线程都有自己的对 sektioner 对象实例的引用副本。 You can't affect other threads that way.您不能以这种方式影响其他线程。 What would happen with old version of data pointed by sektioner field largely depends on calling party. sektioner 字段指向的旧版本数据会发生什么在很大程度上取决于主叫方。

I think, if you want a thread safety, you should use this way:我认为,如果你想要线程安全,你应该使用这种方式:

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 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM