【问题标题】:Alternative to ConcurrentDictionary for portable class library可移植类库的 ConcurrentDictionary 的替代方案
【发布时间】:2013-08-24 10:33:07
【问题描述】:

我正在编写一个面向 .NET 4.5、Windows Store 应用程序和 Windows Phone 8 的可移植类库。我需要一种高效的内存缓存机制,所以我正在考虑使用 ConcurrentDictionary<K,V>,但它在WP8。

会有很多读取和相对较少的写入,所以理想情况下,我想要一个支持多线程无锁读取和单线程写入的集合。非泛型 Hashtable 具有该属性 according to MSDN,但不幸的是它在 PCL 中不可用...

PCL 中是否有其他可用的集合类符合此要求?如果不是,那么在不锁定读取的情况下实现线程安全的好方法是什么? (锁定写入是可以的,因为它不会经常发生)


编辑:感谢 JaredPar 的指导,我最终以完全无锁的方式实现了我的缓存,使用来自 Microsoft.Bcl.ImmutableImmutableDictionary<TKey, TValue>

class Cache<TKey, TValue>
{
    private IImmutableDictionary<TKey, TValue> _cache = ImmutableDictionary.Create<TKey, TValue>();

    public TValue GetOrAdd(TKey key, [NotNull] Func<TKey, TValue> valueFactory)
    {
        valueFactory.CheckArgumentNull("valueFactory");

        TValue newValue = default(TValue);
        bool newValueCreated = false;
        while (true)
        {
            var oldCache = _cache;
            TValue value;
            if (oldCache.TryGetValue(key, out value))
                return value;

            // Value not found; create it if necessary
            if (!newValueCreated)
            {
                newValue = valueFactory(key);
                newValueCreated = true;
            }

            // Add the new value to the cache
            var newCache = oldCache.Add(key, newValue);
            if (Interlocked.CompareExchange(ref _cache, newCache, oldCache) == oldCache)
            {
                // Cache successfully written
                return newValue;
            }

            // Failed to write the new cache because another thread
            // already changed it; try again.
        }
    }

    public void Clear()
    {
        _cache = _cache.Clear();
    }
}

【问题讨论】:

  • 您是否考虑过编写一种包装不可变树的类型,并为写入做了 CAS?这将为您提供无锁读取、CAS 写入,您可以使用 Eric 的不可变 AVL 树作为支持结构blogs.msdn.com/b/ericlippert/archive/2008/01/21/…
  • @JaredPar,什么是“CAS”?
  • CAS = 比较和交换。
  • 谢谢,我会调查的。但是每次我需要添加东西时复制树不是很昂贵吗?

标签: c# caching collections concurrency thread-safety


【解决方案1】:

要考虑的一个选项是在不可变的搜索树上编写一个薄的外观。网络上有几种不可变的搜索树可供选择。我通常以 Eric Lipperts 的精彩帖子为基础

使用它作为后备数据结构将使您免于锁定。对树的写入也可以使用 CAS 以无锁方式完成。这将比ConcurrentDictionary 慢一点,因为查找是 O(Log(N)) 而不是接近 O(1)。但它应该为你解决问题

【讨论】:

  • 很好的解决方案,谢谢!关于无锁写入,我不知道如何实现它......如果我不锁定并且每个写入线程创建一个不同的副本,第二个将覆盖第一个所做的,不是吗?跨度>
  • 这是我现在使用树的方式:gist.github.com/thomaslevesque/92ad1f8643dfa7a2970a
  • @ThomasLevesque 看看我所做的编辑。我试图解释cmets中的逻辑gist.github.com/jaredpar/20fbdb7ad7fbbb4bd82d
  • 好的,我明白了!感谢您的解释。我将不得不对其进行一些修改,因为在我的情况下,创建价值是相当昂贵的,所以我宁愿不要多次这样做。另外,我将 AVLTree 替换为来自 here 的 ImmutableDictionary 类,它具有更好的性能(大约快 30%)
  • 作为记录,这是我的最终版本:gist.github.com/thomaslevesque/ecdd3640a0601768386f。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-14
  • 2015-08-05
  • 1970-01-01
  • 2010-11-29
  • 2012-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多