1

我有一个 .NET 3.5 应用程序,其中 Dictionary 实例在两个线程之间共享。我知道字典本身在任何方面都不是线程安全的,但我只有一个线程可以修改字典,而另一个线程只需要确保它在执行其工作时具有最新的值。(从最严格的意义上讲,即使是最新的也不是硬性要求)

一个线程正在接收间歇性串行数据并调用 Set 函数来更改字典中的值。(此字典在初始设置后是固定大小的,我基本上只是将其用作稀疏数组)

第二个线程收集当前存储在字典中的值,并通过 GetLatestValues() 对它们进行一些处理。

public class HwMemoryMap{
   Dictionary<int, HwDataItem> HwCache;

   public void Set(HwDataItem dataItem){
       HwCache[dataItem.PtId] = dataItem;
       MemoryBarrier();
   }

   public List<HwDataItem> GetLatestValues(){
       System.Threading.Thread.MemoryBarrier();
       List<HwDataItem> HwDataItemList = new List<HwDataItem>();
       // do work here to pull appropriate values out of HwCache
       HwDataItemList.Add(HwCache[0]); // etc
       return HwDataItemList;
   }
}

此处的 MemoryBarrier() 调用是否足以确保对特定键的字典值的更改在所有线程/内核中传播?

我的测试没有发现任何问题,但考虑到这些问题的性质,这并没有给我任何安慰。

4

2 回答 2

2

不,这不安全。您仍然冒着同时从数据结构中写入和读取的风险。如果写入频率足够低,您可以使用一个非常有效的技巧。基本上,您确保引用的数据结构HwCache保持不变。每次要更改数据结构时,首先将其复制到新实例中,然后在独占锁内更改新实例。然后,当您的更改完成后,您将HwCache引用与新实例交换出来。要使其正常工作,您必须标记HwCachevolatile

public class HwMemoryMap
{
  private object lockobj = new object();
  volatile Dictionary<int, HwDataItem> HwCache;

  public void Set(HwDataItem dataItem)
  {
    lock (lockobj)
    {
      var copy = new Dictionary<int, HWDataItem>(HwCache);
      copy[dataItem.PtId] = dataItem;
      HwCache = copy;
    }
  }

  public List<HwDataItem> GetLatestValues()
  {
    var local = HwCache;
    var HwDataItemList = new List<HwDataItem>();
    // do work here to pull appropriate values out of local
    HwDataItemList.Add(local[0]); // etc
    return HwDataItemList;
  }
}
于 2012-04-11T13:55:16.980 回答
1

我对 C# 不是很熟悉,但我不会对“幕后”发生的事情做出假设。特别是,如果插入代码假定它是唯一访问数据结构的代码,它可能会在分配空间或其他情况时留下部分更新的引用。

考虑以下(简化的)代码:

Insert(TKey key, TVal val) {
    if (this.size > this._threshold) {
        // Allocate more space and
        // move to a new table
    }
    // Find location and insert
}

如果所有这些都发生在您的一个商店中(我不是说它是,我不知道 C# 如何实现字典),那么如果任何中间状态传播到不同的状态,内存屏障不会拯救您线。

可能更有意义的是一对读/写锁。如果常见的情况是读,可以让多个线程都抢读锁,只有在需要更新的时候才需要给写线程独占访问。

于 2012-04-11T01:40:52.093 回答