替换与 ConcurrentDictionary 键关联的值是否会锁定该键之外的任何字典操作?
编辑:例如,我想知道除了首次添加密钥时,我想知道其中一个线程是否会阻止另一个线程,如下所示:
public static class Test {
private static ConcurrentDictionary<int, int> cd = new ConcurrentDictionary<int, int>();
public static Test() {
new Thread(UpdateItem1).Start();
new Thread(UpdateItem2).Start();
}
private static void UpdateItem1() {
while (true) cd[1] = 0;
}
private static void UpdateItem2() {
while (true) cd[2] = 0;
}
}
最初我假设它确实如此,因为例如dictionary[key] = value;
可以引用尚不存在的键。但是,在工作时,我意识到如果需要添加,则可能会在单独的锁定升级后发生。
我正在起草下面的课程,但如果这个问题(上面)的答案是"否",那么AccountCacheLock
类提供的间接信息是不必要的。事实上,我自己的所有锁管理几乎都是不需要的。
// A flattened subset of repository user values that are referenced for every member page access
public class AccountCache {
// The AccountCacheLock wrapper allows the AccountCache item to be updated in a locally-confined account-specific lock.
// Otherwise, one of the following would be necessary:
// Replace a ConcurrentDictionary item, requiring a lock on the ConcurrentDictionary object (unless the ConcurrentDictionary internally implements similar indirection)
// Update the contents of the AccountCache item, requiring either a copy to be returned or the lock to wrap the caller's use of it.
private static readonly ConcurrentDictionary<int, AccountCacheLock> dictionary = new ConcurrentDictionary<int, AccountCacheLock>();
public static AccountCache Get(int accountId, SiteEntities refreshSource) {
AccountCacheLock accountCacheLock = dictionary.GetOrAdd(accountId, k => new AccountCacheLock());
AccountCache accountCache;
lock (accountCacheLock) {
accountCache = accountCacheLock.AccountCache;
}
if (accountCache == null || accountCache.ExpiresOn < DateTime.UtcNow) {
accountCache = new AccountCache(refreshSource.Accounts.Single(a => a.Id == accountId));
lock (accountCacheLock) {
accountCacheLock.AccountCache = accountCache;
}
}
return accountCache;
}
public static void Invalidate(int accountId) {
// TODO
}
private AccountCache(Account account) {
ExpiresOn = DateTime.UtcNow.AddHours(1);
Status = account.Status;
CommunityRole = account.CommunityRole;
Email = account.Email;
}
public readonly DateTime ExpiresOn;
public readonly AccountStates Status;
public readonly CommunityRoles CommunityRole;
public readonly string Email;
private class AccountCacheLock {
public AccountCache AccountCache;
}
}
附带问题:ASP.NET 框架中是否有已经这样做的东西?
你不需要做任何锁。ConcurrentDictionary应该可以很好地处理这个问题。
附带问题:ASP.NET 框架中是否有已经这样做的东西?
答案是肯定的。它与 ASP.NET 没有特别的关系,但您可以查看System.Runtime.Caching
命名空间,更具体地说是MemoryCache
类。它在线程安全哈希表的顶部添加了过期和回调等内容。
我不太明白您在更新的答案中显示的AccountCache
的目的。这正是简单的缓存层免费为您提供的。
显然,如果您打算在 Web 场中运行 ASP.NET 应用程序,您应该考虑一些分布式缓存,例如 memcached。在memcached协议之上有ObjectCache类的.NET实现。
想指出,我在ConcurrentDictionary中粗略地看了一眼,看起来项目替换既没有锁定单个项目,也没有锁定整个字典,而是锁定了项目的哈希(即与字典"桶"关联的锁定对象)。它的设计似乎使密钥的初始引入也不会锁定整个字典,前提是字典不需要调整大小。我相信这也意味着两个更新可以同时发生,只要它们不产生匹配的哈希。