c# – ConcurrentDictionary.GetOrAdd – 仅在不为null时添加

前端之家收集整理的这篇文章主要介绍了c# – ConcurrentDictionary.GetOrAdd – 仅在不为null时添加前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在使用ConcurrentDictionary通过并行访问来缓存数据,有时新项目可以存储在db中,并且它们不会加载到缓存中.这就是我使用GetOrAdd的原因
  1. public User GetUser(int userId)
  2. {
  3. return _user.GetOrAdd(userId,GetUserFromDb);
  4. }
  5.  
  6. private User GetUserFromDb(int userId)
  7. {
  8. var user = _unitOfWork.UserRepository.GetById(userId);
  9.  
  10. // if user is null,it is stored to dictionary
  11.  
  12. return user;
  13. }

但是,如果用户不为空,我如何检查用户是否从db获取并将用户存储到字典?

可能我可以在GetOrAdd之后立即从ConcurrentDictionary中删除null但它看起来不是线程安全的并且它不是非常优雅的解决方案.无用插入和从字典中删除.你知道怎么做吗?

解决方法

  1. public User GetUser(int userId)
  2. {
  3. var user = _user.GetOrAdd(userId,GetUserFromDb);
  4. if (user == null) _user.TryRemove(userId,out user);
  5. }

您还可以将其包装到扩展方法中:

  1. public static TValue GetOrAddIfNotNull<TKey,TValue>(
  2. this ConcurrentDictionary<TKey,TValue> dictionary,TKey key,Func<TKey,TValue> valueFactory) where TValue : class
  3. {
  4. var value = dictionary.GetOrAdd(key,valueFactory);
  5. if (value == null) dictionary.TryRemove(key,out value);
  6. return value;
  7. }

然后你的代码看起来像:

  1. public User GetUser(int userId)
  2. {
  3. var user = _user.GetOrAddIfNotNull(userId,GetUserFromDb)
  4. }

UPDATE

根据@usr评论,可能有以下情况:

>线程1执行GetOrAdd,将null添加到字典并暂停.
>用户添加数据库中.
>线程2执行GetOrAdd并从字典中检索null而不是命中数据库.
>线程1和线程2执行TryRemove并从字典中删除记录.

有了这个时间,线程2将变为null而不是命中数据库获取用户记录.如果这个边缘情况对您很重要并且您仍然想使用ConcurrentDictionary,那么您可以在扩展方法中使用lock:

  1. public static class ConcurrentDictionaryExtensions
  2. {
  3. private static readonly object myLock = new object();
  4.  
  5. public static TValue GetOrAddIfNotNull<TKey,TValue>(
  6. this ConcurrentDictionary<TKey,TValue> valueFactory) where TValue : class
  7. {
  8. lock (myLock)
  9. {
  10. var value = dictionary.GetOrAdd(key,valueFactory);
  11. if (value == null) dictionary.TryRemove(key,out value);
  12. return value;
  13. }
  14. }
  15. }

猜你在找的C#相关文章