【问题标题】:increase efficiency of concurrent dictionary access c#提高并发字典访问c#的效率
【发布时间】:2012-04-23 19:43:21
【问题描述】:

我有一个由多个后台线程访问以更新/读取的列表。更新操作包括插入和删除。

为了在不出现同步问题的情况下同时执行此操作,我在类中的私有只读对象上使用了锁。

为了最大程度地减少读取数据时锁定列表所需的时间,我对其进行深度克隆并返回深度克隆并解锁字典以进行插入/删除更新。

因此,每次读取列表都会增加我的服务的内存消耗。

需要注意的一点是,插入/删除是包含列表的类的内部。但该读物是供公众阅读的。

我的问题是:

有什么办法,我可以避免克隆列表并仍然使用读/写锁同时使用它进行读取?

public class ServiceCache
    {
        private static List<Users> activeUsers;
        private static readonly object lockObject = new object();
        private static ServiceCache instance = new ServiceCache();

        public static ServiceCache Instance
        {
            get
            {
                return instance;
            }
        }

        private void AddUser(User newUser)
        {
            lock (lockObject)
            {
                //... add user logic
            }
        }

        private void RemoveUser(User currentUser)
        {
            lock (lockObject)
            {
                //... remove user logic
            }
        }

        public List<Users> ActiveUsers
        {
            get
            {
                lock (lockObject)
                {
                    //The cache returns deep copies of the users it holds, not links to the actual data.
                    return activeUsers.Select(au => au.DeepCopy()).ToList();
                }
            }
        }
    }

【问题讨论】:

  • 如果独立线程正在从列表的深层副本中删除,您如何确保他们的工作有意义地同步?
  • Eric,深拷贝只是为了及时作为缓存的快照,改变它不应该反映在实际的缓存中。
  • 旁注:“为了缩短时间……我对它进行了深度克隆”:这就是“过早优化万恶之源”这一短语存在的确切原因。任何重要对象的深度克隆都不太可能是快速操作。您始终应该衡量以了解您的“优化”实际上是什么。

标签: c# list c#-4.0 concurrency locking


【解决方案1】:

听起来您需要使用ConcurrentDictionary 类,并为要存储的每个Users 对象创建一个键。然后添加/更新用户就这么简单了:

_dictionary.AddOrUpdate("key", (k, v) =>
    {
        return newUser;
    }, (k, v) =>
    {
        return newUser;
    });

然后要删除,您可以这样做:

 Users value = null;
_dictionary.TryRemove("key", out value);

获取人员列表也非常容易,因为您只需要这样做:

return _dictionary.Values.Select(x => x.Value).ToList();

此时应该返回字典内容的副本。

让 .NET 运行时为您处理线程。

【讨论】:

  • 其实可以直接返回.Values,即copy-on-read。
  • 有趣,我没有意识到这一点!现在我读到它完全有意义=D
【解决方案2】:

您可以使用读写锁来允许同时读取。

但是,使用ConcurrentDictionary 和线程安全的不可变值会快得多,然后摆脱所有同步。

【讨论】:

  • 即使锁是该类的本地锁,但我正在访问它锁定在外部以供读取的对象,这有关系吗?
【解决方案3】:

因此,每次读取列表都会增加 我的服务。

为什么?调用者没有释放引用吗?他们需要这样做,因为字典的内容可以改变。

我认为你正在做的复制非常接近并发数据结构,例如写时复制收集有效,但调用者无法保留引用。

其他几种方法:

  • 将相同的副本返回给所有调用者,直到集合被修改。返回的集合应该是不可变的

  • 公开调用者希望从副本中获得的所有功能,并使用单个锁来处理原始列表

【讨论】:

    猜你喜欢
    • 2020-04-21
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 2020-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多