简体   繁体   English

访问将在C#中的另一个线程中更新的参考对象是否安全?

[英]Is it safe to access a reference object that will be updated in another thread in C#?

There were some similar questions on stackoverflow about that, but I did not quite get the answer to the case that I have at the moment: 关于stackoverflow也有一些类似的问题,但是对于目前的情况,我还没有得到答案:

  • I have some kind of a cache in a dictionary that is accessed in a multiple threads 我在多线程访问的字典中有某种缓存
  • It does not matter if aa thread access the "old" version if an update is in process 如果正在进行更新,则线程是否访问“旧”版本都没有关系

    This is how I access the dictionary: 这是我访问字典的方式:

     if (m_Dictionary != null && m_Dictionary.ContainsKey(key)) { return await Task.FromResult(m_Dictionary[key]); } 

    This is how I update the reference when some element is not in the cache eg (I can have multiple updates): 当某些元素不在缓存中时,这就是我如何更新引用的方式(例如(我可以进行多次更新):

     var newDictionary = Load(); lock (m_Lock) { m_Dictionary = newDictionary; } 

So the question is: will I run into some issues with this, namely reading some partially updated reference? 所以问题是:我是否会遇到一些问题,即阅读一些部分更新的参考书? Since it's not 32 bit value (it can run on 64 bit architecture) it seems to me that it's not safe as atomicity is not guaranteed. 由于它不是32位值(它可以在64位体系结构上运行),在我看来这是不安全的,因为不能保证原子性。 Will it be enough then to change all the read access from variable to this method: 然后将所有读取访问权限从变量更改为此方法是否足够:

private Dictionary<string, int> GetDictionary()
{
    lock (m_Lock)
    {
       return m_Dictionary;
    }
}

This part is unsafe: 这部分是不安全的:

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]);
}

for that reason you would have to surround it with a lock (m_Lock){} too. 因此,您也必须用lock (m_Lock){}将其包围。

But your requirements should be met with: 但是您的要求应该满足:

var localDictionary = GetDictionary();  // a local copy is thread-safe

if (localDictionary.ContainsKey(key))   // or TryGetvalue
{
    return await Task.FromResult(localDictionary[key]);
}
else ...

Since you're updating the entire dictionary in one go, the actual access of the reference is safe. 由于您要一次性更新整个词典,因此对引用的实际访问是安全的。

Since it's not 32 bit value (it can run on 64 bit architecture) it seems to me that it's not safe as atomicity is not guaranteed. 由于它不是32位值(它可以在64位体系结构上运行),在我看来这是不安全的,因为不能保证原子性。

On a 64-bit architecture, 64-bit-aligned 64-bit values have atomic access. 在64位体系结构上,以64位对齐的64位值具有原子访问权限。 Reference accesses are always atomic. 引用访问始终是原子的。

However, there are two things that can still go wrong: 但是,仍然有两件事可能会出错:

One of these is updates from one core not being seen with another. 其中之一是来自一个核心的更新没有被另一个核心看到。 You don't have this issue since you write in a lock and .NET locks have an implicit memory barrier. 您没有此问题,因为您在锁中进行了写入,并且.NET锁具有隐式的内存屏障。 (Indeed, you don't need a full lock, a volatile write will suffice). (实际上,您不需要完全锁定,只需进行易失性写入即可)。

The other is races between reads. 另一个是读取之间的种族。 the m_dictionary of one read may not be the m_dictionary of the next. 一次读取的m_dictionary可能不是下一次读取的m_dictionary Read it into a local variable, and then act upon that local. 将其读入局部变量,然后对该局部变量进行操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM