ConcurrentDictionary 使用安全吗?

Is ConcurrentDictionary safe to use?

我在不同的线程中有三个不同的代码 运行。

线程任务 1:从设备读取数据并将其写入 ConcurrentDictionary

线程任务2:将ConcurrentDictionary中的数据作为单独的文件写入计算机。

我在论坛上看过很多帖子,说 concurrentdictionary 对于单独的线程是安全的。我还读到有锁定情况。其实我脑子里不止一个问号

concurrentdictionary需要加锁吗?在哪些情况下需要锁定?如果需要加锁怎么办?以下方式使用会导致什么问题?

线程代码1:每秒都有数据进来

public void FillModuleBuffer(byte[] buffer, string IpPort)
{
     if (!CommunicationDictionary.dataLogList.ContainsKey(IpPort))
     {
         CommunicationDictionary.dataLogList.TryAdd(IpPort, buffer);
     }
}

线程代码 2: 下面的代码在定时器中工作。计时器持续时间为 200 毫秒。

if (CommunicationDictionary.dataLogList.ContainsKey(IpPort))
        {
          using (stream = File.Open(LogFilename, FileMode.Append, FileAccess.Write))
          {
              using (BinaryWriter writer = new BinaryWriter(stream))
              {
                 writer.Write(CommunicationDictionary.dataLogList[IpPort]);
                 writer.Flush();
                 writer.Close();
                 CommunicationDictionary.dataLogList.TryRemove(IpPort,out _);
               }
          }

}

注意:为清楚起见,对代码进行了简化。

注2:我之前用的是Dictionary。我遇到了一个非常不同的问题。处于活动状态时,2-3 小时后,我收到错误消息,即数组超出索引范围,即使 Dictionary.

中没有数据

示例代码应该是线程安全的,但它显示了对如何使用并发字典的误解。例如:

 if (!CommunicationDictionary.dataLogList.ContainsKey(IpPort))
 {
     CommunicationDictionary.dataLogList.TryAdd(IpPort, buffer);
 }

这恰好可行,因为只有一个线程添加到字典中,但由于有单独的语句,字典可能会在它们之间发生变化。如果您查看 TryAdd 的文档,您会发现如果密钥已经存在,它将 return false。所以不需要ContainsKey。有很多不同的方法,目的是同时做多件事,以确保整个操作是原子的。

与阅读线程相同。对 concurrentDictionary 的所有访问都应替换为对 TryRemove

的一次调用
if (CommunicationDictionary.dataLogList.TryRemove(IpPort,out var data))
    {
      using (stream = File.Open(LogFilename, FileMode.Append, FileAccess.Write))
      {
          using (BinaryWriter writer = new BinaryWriter(stream))
          {
             writer.Write(data);
             writer.Flush();
             writer.Close();
           }
      }
}

请注意,这将保存一些数据块,并丢弃其他数据块,没有任何硬性保证将保存或不保存哪些块。这可能是预期的行为,但对于确保保存所有数据的队列来说更常见。一个典型的实现会用一个或多个生产线程和一个消费线程来包装 concurrentQueue in a blockingCollection。这避免了对单独计时器的需要。