0

替换与 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 框架中是否有已经这样做的东西?

4

2 回答 2

2

你不需要做任何锁。ConcurrentDictionary 应该可以很好地处理这个问题。

附带问题:在 ASP.NET 框架中是否有已经这样做的东西?

当然。它与 ASP.NET 没有特别相关,但您可以查看System.Runtime.Caching命名空间,更具体地说是MemoryCache类。它在线程安全哈希表的顶部添加了过期和回调等内容。

我不太明白AccountCache您在更新的答案中显示的目的。这正是一个简单的缓存层免费为您提供的。

显然,如果您打算在 Web 场中运行 ASP.NET 应用程序,您应该考虑一些分布式缓存,例如 memcached。在 memcached 协议之上有 ObjectCache 类的 .NET 实现。

于 2013-12-22T11:23:56.407 回答
0

我还想指出,我粗略地看了一下 ConcurrentDictionary,看起来项目替换既不是锁定在单个项目上,也不是锁定在整个字典上,而是锁定在项目的哈希上(即与字典“桶”关联的锁定对象”)。它似乎被设计成最初引入一个键也不会锁定整个字典,前提是不需要调整字典的大小。我相信这也意味着两个更新可以同时发生,只要它们不产生匹配的哈希值。

于 2013-12-22T14:15:35.950 回答