2008-09-22 73 views
0

我得到了这个síngleton缓存对象,它公开了一个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 
    } 
} 

回答

1

当前正在枚举sektioner的线程将继续枚举它,即使您更新单例内的引用。实现IEnumerable的对象没有什么特别之处。

您应该将volatile关键字添加到sektioner字段,因为您没有提供读取锁定,并且多个线程正在读取/写入它。

+0

它正在枚举的实例不需要是新的(和易失性关键字)?我的意思是我让它IEnumerable所以它不能改变任何东西,或者我在这里错过了什么? – 2008-09-22 10:49:30

3

由于吸气{ return sektioner; }被称为放在前场的新值,返回旧的值。然后,循环foreach (Sektion s in cache.Sektioner)使用调用getter时收到的值,即旧值。该值将在整个foreach循环中使用。

+0

是啊,这就是我想太多,因为它会在任何其他情况下,不过这种特殊情况下提出了一个问题在审查我的对象期间,所以我想确定我是否没有错过任何东西。 – 2008-09-22 10:46:39

0

首先我看不到对象锁定,未使用的lockObject变量让我难过。 IEnumerable并不特殊。每个线程都有它自己的一个引用sektioner对象实例的副本。你不能以这种方式影响其他线程。使用sektioner字段指向的旧版本数据会发生什么情况很大程度上取决于主叫方。

+0

锁定位于我的UpdateCache的'SNIP'部分:) – 2008-09-22 10:48:00

0

我想,如果你想有一个线程安全的,你应该用这样的方式:

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