0

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

4 回答 4

3

由于{ return sektioner; }在将新值放入字段之前调用了 getter,因此返回了旧值。然后,循环foreach (Sektion s in cache.Sektioner)使用调用 getter 时收到的值,即旧值。该值将在整个 foreach 循环中使用。

于 2008-09-22T10:42:16.877 回答
1

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

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

于 2008-09-22T10:47:14.867 回答
0

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

于 2008-09-22T10:45:23.560 回答
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
    //}
}
于 2008-09-22T12:05:54.193 回答