这个ConcurrentDictionary线程安全吗?

时间:2018-07-02 14:21:11

标签: c#

这是ConcurrentDictionary ThreadSafe吗?

    private static ConcurrentDictionary<string, DateTime> dictionary= new ConcurrentDictionary<string, DateTime>();

    public bool TextInputRecently(string text)
    {
        dictionary = new ConcurrentDictionary<string, DateTime>( dictionary.Where(pair => pair.Value.Ticks >= DateTime.Now.AddMinutes(-5).Ticks)
                             .ToDictionary(pair => pair.Key,
                                           pair => pair.Value));

        if (dictionary.ContainsKey(text))
            return true;

        dictionary.TryAdd(text, DateTime.Now);
        return false;
    }

我认为不是这样,因为可以在另一个线程检查键是否存在的同时重新创建字典。

我会更好地遍历字典,删除过时的值吗?

2 个答案:

答案 0 :(得分:4)

否;字典可能会在ContainsKey()TryAdd()之间改变。

除非您确定自己不在乎它们之间是否发生变化,否则切勿在{{1​​}}上连续调用两个方法。
同样,您也无法循环浏览字典,因为字典可能会在循环过程中发生变化。

相反,您应该使用其更复杂的方法(例如ConcurrentDictionary,它将检查并添加单个原子操作。

另外,正如您所建议的,整个字典可能会被替换。

答案 1 :(得分:0)

感谢答案。

我遵循了Scott Hannen的建议,并用MemoryCache代替了它。如果有人要的话,以下是这些代码,请紧跟Locking pattern for proper use of .NET MemoryCache

private static MemoryCache cache;
    static readonly object cacheLock = new object();
    private int expiry_timeout = 5;

    public bool TextInputRecently(string text)
    {
        //Returns null if the string does not exist, prevents a race condition where the cache invalidates between the contains check and the retrieval.
        var cachedString = MemoryCache.Default.Get(text, null) as string;

        if (cachedString != null)
            return true;

        lock (cacheLock)
        {
            //Check to see if anyone wrote to the cache while we where waiting our turn to write the new value.
            cachedString = MemoryCache.Default.Get(text, null) as string;

            if (cachedString != null)
                return true;

            //The value still did not exist so we now write it in to the cache.                
            CacheItemPolicy cip = new CacheItemPolicy()
            {
                AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(expiry_timeout))
            };
            MemoryCache.Default.Set(text, "", cip);
            return false;
        }
    }        

也许有更好的解决方案,但这可以满足我的需求。