使用相同的键/值从多个线程更新字典是否安全
我有一个静态 System.Collections.Generic.Dictionary
用作缓存。在缓存未命中时,我会执行一些昂贵的工作来获取指定键的值并将其保存在缓存中:
private static Dictionary<K, V> _cache = ...
public static V GetValue<K>(K key)
{
V value;
if (!_cache.TryGetValue(key, out value))
{
value = GetExpensiveValue(key);
_cache[key] = value; // Thread-safety issue?
}
return value;
}
这是一种常见模式。问题是:当多个线程尝试同时调用 GetValue() 时,这是否安全或者可能会失败?
假设 GetExpectiveValue 始终为给定键返回相同的值,我并不真正关心哪个线程赢得竞争条件。是的,这意味着昂贵的操作可能会比需要的次数多执行一次,但如果读取比写入频繁得多,那么我可能会对此感到满意,并且这允许我消除锁定。但词典内部真的会出问题吗?也就是说,在某些情况下它会抛出异常或损坏其数据吗?我用一个简单的控制台应用程序对此进行了测试,没有发现任何问题,但这当然并不意味着它总是有效。
I have a static System.Collections.Generic.Dictionary<K, V>
that's used as a cache. On a cache miss I do some expensive work to get the value for the specified key and save it in the cache:
private static Dictionary<K, V> _cache = ...
public static V GetValue<K>(K key)
{
V value;
if (!_cache.TryGetValue(key, out value))
{
value = GetExpensiveValue(key);
_cache[key] = value; // Thread-safety issue?
}
return value;
}
This is a common pattern. The question is: is this safe or could it fail when multiple threads try to call GetValue()
at the same time?
Assuming that GetExpensiveValue
always returns the same value for a given key, I don't really care which thread wins the race condition. Yes, it means that the expensive operation is potentially done one more time than needed, but that's a trade-off I may be happy with if reads are far more frequent than writes and this allows me to eliminate locking. But would something actually go wrong inside the Dictionary? That is, would it throw an exception or corrupt its data in some cases? I tested this out with a simple console app and found no issues, but of course that doesn't mean it will always work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对我来说这看起来不是线程安全的;字典的内部可能已经在被重写的过程中。 至少它可能需要一个读/写锁:
如果您只想
GetExpectiveValue(key)
执行一次,请将其移入写锁并添加双重检查:That doesn't look thread-safe to me; the inside of the dictionary could already be in the process of being re-written. At a minimum it probably needs a reader/writer lock:
and if you only want
GetExpensiveValue(key)
to execute once, move it inside the write-lock and add a double-check:如果你不关心竞争条件那么你应该没问题。如果您关心昂贵的操作,只需在
TryGetvalue()
之后锁定
,然后再次调用TryGetValue()
编辑:
考虑到Marc的评论后,我认为上述并不是最好的选择。也许考虑使用 ConcurrentDictionary 代替。
If you don't care about race conditions then you should be fine. If you care about the expensive operation, just
lock
afterTryGetvalue()
and then callTryGetValue()
againEdit:
After consideration of Marc's comment, I think the above is not the best option. Perhaps consider using the ConcurrentDictionary instead.