2013-12-22 15 views
0

替換與ConcurrentDictionary關鍵字關聯的值是否會鎖定超出該關鍵字的任何字典操作?在不同的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框架已經這樣做?

+0

什麼確切的鎖定將依靠?我還不完全明白你想要達到什麼目標...... –

+0

嘿喬恩。我不確定我是否理解你的澄清要求;我很確定它缺少一個詞,但我無法弄清楚它是什麼。我會嘗試重申:我正在尋找替換字典中的項目,而不阻止添加其他項目,或阻止讀取或替換其他鍵控值。我是否需要在我上面的專有代碼中使用'AccountCacheLock'添加的額外鎖定層,或者我可以簡單地調用'ConcurrentDictionary [key] ='? – shannon

+0

最終,我只是試圖爲帳戶ID上鍵入的常用帳戶(和子)信息創建緩存。我擔心,如果我只是要求併發字典替換一個項目,我會在替換期間阻止所有線程使用緩存。 – shannon

回答

2

你不需要做任何鎖定。 ConcurrentDictionary應該處理得很好。

旁邊的問題:有沒有什麼在ASP.NET框架中已經這樣做?

當然。它與ASP.NET沒有特別的關係,但您可以查看System.Runtime.Caching命名空間,更具體地看MemoryCache類。它在線程安全哈希表的頂部添加了諸如到期和回調之類的東西。

我不完全理解您在更新的答案中顯示的AccountCache的用途。這正是簡單的緩存層免費提供的。

顯然,如果您打算在Web場中運行ASP.NET應用程序,則應考慮使用一些分佈式緩存,例如memcached。在memcached協議之上有ObjectCache類的.NET實現。

+0

我在我的問題中找不到'AccountClass'。你可能意味着'AccountCache'。如果是這樣,它的目的不僅是持有Account對象,還有一些相關的信息,從一對夫婦相關的對象和安全規則中崩潰。此外,它旨在避免緩存整個帳戶對象,因爲很少帳戶屬性經常被引用。緩存不必要的屬性(特別是完整的層次結構)不僅會增加內存開銷,而且更重要的是增加項目在簡單緩存實現中失效的頻率。我明白了嗎? – shannon

+0

是的,我完全可以複製MemoryCache類中存在的功能。我不反對。我要去調查。 – shannon

+1

對不起我的壞。我的意思是'AccountCache'。如果整個Account對象太大而無法緩存,請考慮使用它的一個視圖(視圖模型),該視圖將與MemoryCache一起緩存。恕我直言緩存是困難的,我會建議使用框架內置的類,而不是試圖推出自己的。 –

0

我也想注意到,我在ConcurrentDictionary中粗略地窺視了一下,它看起來像是項目替換不是鎖定在單個項目上,也不是鎖定在整個字典上,而是項目的哈希(即與字典「桶」)。它似乎被設計成初始引入密鑰也不會鎖定整個字典,前提是字典不需要調整大小。我相信這也意味着兩個更新可以同時發生,只要它們不會產生匹配的哈希值。

相關問題