关于stackoverflow有一些类似的问题,但我现在还没有完全得到答案:
如果正在进行更新,一个线程是否访问“旧”版本无关紧要
这是我访问字典的方式:
if (m_Dictionary != null && m_Dictionary.ContainsKey(key))
{
return await Task.FromResult(m_Dictionary[key]);
}
这是当一些元素不在缓存中时更新引用的方式,例如(我可以有多个更新):
var newDictionary = Load();
lock (m_Lock)
{
m_Dictionary = newDictionary;
}
所以问题是:我会遇到一些问题,即阅读一些部分更新的参考?由于它不是32位值(它可以在64位架构上运行),在我看来它不安全,因为原子性不能得到保证。那么将所有读取访问权限从变量更改为此方法就足够了:
private Dictionary<string, int> GetDictionary()
{
lock (m_Lock)
{
return m_Dictionary;
}
}
答案 0 :(得分:2)
这部分不安全:
if (m_Dictionary != null && m_Dictionary.ContainsKey(key))
{
// this could still throw KeyNotFound because
// it can be a different Dictionary than in the previous line
return await Task.FromResult(m_Counterparts[key]);
}
因此你必须用lock (m_Lock){}
包围它。
但是你的要求应该得到满足:
var localDictionary = GetDictionary(); // a local copy is thread-safe
if (localDictionary.ContainsKey(key)) // or TryGetvalue
{
return await Task.FromResult(localDictionary[key]);
}
else ...
答案 1 :(得分:2)
由于您一次性更新整个字典,因此实际访问该引用是安全的。
由于它不是32位值(它可以在64位架构上运行),在我看来它不安全,因为原子性不能得到保证。
在64位体系结构中,64位对齐的64位值具有原子访问权限。参考访问总是原子的。
然而,有两件事仍然可能出错:
其中一个是来自一个核心的更新,而不是另一个核心。由于您在锁中写入并且.NET锁具有隐式内存屏障,因此您不会遇到此问题。 (实际上,你不需要一个完整的锁定,一个易变的写入就足够了。)
另一个是阅读之间的比赛。一次阅读的m_dictionary
可能不是下一次的m_dictionary
。将其读入本地变量,然后对该本地变量进行操作。