我有一个列表,可供多个后台线程访问以进行更新/读取。更新操作包括插入和删除。
为了在没有同步问题的情况下同时执行此操作,我在类中使用了一个私有只读对象的锁。
为了最大限度地减少在读取数据时需要锁定列表的时间,我会对其进行深度克隆并返回深度克隆并解锁字典以进行插入/删除更新。
由于这一点,每次读取列表都会增加我服务的内存消耗。
需要注意的一点是,插入/删除是包含列表的类的内部。但阅读是为了公共消费。
我的问题是:
有什么办法,我可以避免克隆列表并仍然使用它同时使用读/写锁读取?
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();
}
}
}
}
答案 0 :(得分:6)
听起来您需要使用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运行时为您处理线程。
答案 1 :(得分:5)
您可以使用读写器锁定来同时读取。
但是,使用ConcurrentDictionary
和线程安全的不可变值会更快,然后摆脱所有同步。
答案 2 :(得分:1)
由于这一点,每次读取列表都会增加内存消耗 我的服务。
为什么呢?呼叫者是否未发布参考?他们需要,因为字典的内容可以改变。
你在复制时做的事情我认为非常接近并发数据结构,例如copy-on-write集合有效,但调用者无法保留引用。
其他几种方法:
将相同的副本返回给所有调用者,直到修改了集合。返回的集合应该是不可变的
从副本中公开调用者想要的所有功能,并使用单个锁来处理原始列表