更新对正在使用的成员变量的引用
我得到了这个sngleton缓存对象,它公开了一个IEnumerable属性,该属性仅返回一个私有IEnumerable变量。
我在我的单例对象上有一个静态方法来更新此成员变量(存在于此缓存对象的单个"实例"实例上)。
假设当前正在更新我的缓存时,某个线程正在对该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 } }
解决方案
由于getter{return sektioner;在将新值放入字段之前,将调用}
,然后返回旧值。然后,循环" foreach(Sektioner在cache.Sektioner中的Sektion s)"使用调用getter时收到的值,即旧值。该值将在整个foreach循环中使用。
首先,我看不到对象锁定,未使用的lockObject变量让我很难过。 IEnumerable并不特殊。每个线程将拥有自己的对sektioner对象实例的引用的副本。我们不能以这种方式影响其他线程。 sektioner字段所指向的旧数据会发生什么情况很大程度上取决于呼叫方。
即使我们在单例中更新引用,当前正在枚举sektioner的线程也将继续枚举它。实现IEnumerable的对象没有什么特别的。
我们可能应该将volatile关键字添加到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 //} }