这是C#弱实习缓存表线程安全和memorysafe的实现吗?

时间:2017-11-19 17:34:25

标签: c# multithreading caching weak-references

我在互联网上搜索了很多,以找到一个C#实现,它可以允许使用弱引用来缓存不可变对象,以减少内存占用(类似于.NET String.Intern)。

就我而言,我需要缓存多个产品列表,每个列表中都可以显示相同的产品:

  • 数据库中的产品总数为7百万
  • 我们在加载所有内容后,内存中有1000万个对象
  • 删除重复项后,它会降至3百万,这意味着缓存这些对象会显着减少内存占用。

我找到了一个旧帖子,要求提供此类功能,但没有人能够提出实施Any weak interning collections (for immutable objects)

我已经实现了一个实现,但我需要确保它已准备就绪并且此代码中没有隐藏竞争条件或其他线程问题。

// Inspired by: http://www.nesterovsky-bros.com/weblog/2014/01/08/WeakTable.aspx
public static class ObjectCache<T> where T : class
{
    #region Inner classes

    public enum ElementState : byte
    {
        Uninitialized = 0,
        Initialized = 1,
        Finalized = 2
    }

    private class Element
    {
        public readonly T Value;
        public ElementState State;

        public Element(T value)
        {
            State = ElementState.Uninitialized;
            Value = value;
        }

        ~Element()
        {
            if (State == ElementState.Initialized)
            {
                State = ElementState.Finalized;
                WeakReference<Element> state;
                Values.TryRemove(this, out state);
            }
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }
    }

    private class WeakEqualityComparer : IEqualityComparer<object>
    {
        private readonly IEqualityComparer<T> _innerComparer;

        public WeakEqualityComparer(IEqualityComparer<T> innerComparer)
        {
            _innerComparer = innerComparer;
        }

        public new bool Equals(object x, object y)
        {
            if (x == y) return true;

            var xRef = x as WeakReference<Element>;
            var xElem = xRef == null ? x as Element : GetTarget(xRef);

            var yRef = y as WeakReference<Element>;
            var yElem = yRef == null ? y as Element : GetTarget(yRef);

            if (xElem == yElem) return true;
            if (xElem == null || yElem == null) return false;

            // If xState is finalized, then we are within the Finalizer and we want to
            // search by reference to specifically remove this item.
            if (xElem.State == ElementState.Finalized) return false;

            // If we are looking for an element but this one is currently being finalized,
            // we dont want to return it because it is going to disappear from the cache soon.
            if (xElem.State == ElementState.Uninitialized && yElem.State == ElementState.Finalized) return false;

            return _innerComparer.Equals(xElem.Value, yElem.Value);
        }

        public int GetHashCode(object obj)
        {
            return obj is WeakReference<Element>
                ? (GetTarget((WeakReference<Element>) obj)?.GetHashCode() ?? obj.GetHashCode())
                : obj.GetHashCode();
        }
    }
    #endregion Inner classes

    private static readonly ConcurrentDictionary<object, WeakReference<Element>> Values = new ConcurrentDictionary<object, WeakReference<Element>>(new WeakEqualityComparer(EqualityComparer<T>.Default));

    public static int Count => Values.Count;

    public static T Intern(T item)
    {
        if (item == null)
            return null;

        Element initialElement = new Element(item);
        WeakReference<Element> weakInitialState = new WeakReference<Element>(initialElement, true);
        Element newElement;
        do
        {
            newElement = GetTarget(Values.GetOrAdd(weakInitialState, x =>
            {
                initialElement.State = ElementState.Initialized;
                return weakInitialState;
            }));
        } while (newElement == null);

        return newElement.Value;
    }

    public static TRef GetTarget<TRef>(WeakReference<TRef> value) where TRef : class
    {
        TRef target;
        value.TryGetTarget(out target);
        return target;
    }
}

在这段代码中,有一个WeakReference,其中trackRessurection对每个项都有活动,当其中一个被垃圾收集时,最终的被捕获并用于从ConcurrentDictionary中清除它自己。我发现了以下可能存在问题的案例:

  • 两个线程尝试同时添加相同的项目(应由ConcurrentDictionary处理)
  • 一个项目正在最终确定,而另一个线程试图实习相同的项目(应该由Equals和while循环处理)

还有其他奇怪的情况吗?

0 个答案:

没有答案