使用双缓冲区技术进行并发读写?

Bur*_*tto 5 c# parallel-processing concurrency multithreading multiprocessing

我有一个相对简单的案例:

  1. 我的程序将通过 Websockets 接收更新,并将使用这些更新来更新其本地状态。这些更新将非常小(通常 < 1-1000 字节 JSON,因此反序列化 < 1 毫秒),但会非常频繁(高达 ~1000/s)。
  2. 同时,程序将从该本地状态读取/评估并输出其结果。
  3. 这两个任务应该并行运行,并且将在程序的持续时间内运行,即永远不会停止。
  4. 本地状态大小相对较小,因此内存使用量不是一个大问题。

棘手的部分是更新需要“原子地”发生,这样它就不会从本地状态读取,例如,只写入了一半的更新。状态不限于使用原语,并且可以包含任意类 AFAICT atm,因此我无法通过使用Interlocked原子操作之类的简单方法来解决它。我计划在其自己的线程上运行每个任务,因此在本例中总共有两个线程。

为了实现这个目标,我想使用双缓冲区技术,其中:

  1. 它保留状态的两份副本,以便在写入另一份时可以读取一份。
  2. 线程可以通过使用锁来传达它们正在使用哪个副本。即 Writer 线程在写入时锁定副本;读取器线程在完成当前副本后请求访问锁;编写器线程看到读取器线程正在使用它,因此它切换到其他副本。
  3. 写入线程会跟踪在当前副本上完成的状态更新,因此当它切换到另一个副本时,它可以“赶上”。

这是这个想法的总体要点,但实际的实现当然会有点不同。

我试图查找这是否是一个常见的解决方案,但找不到太多信息,所以这让我想知道以下内容:

  1. 它可行吗,还是我错过了什么?
  2. 有更好的方法吗?
  3. 这是一个通用的解决方案吗?如果有的话,通常被称为什么?
  4. (奖励)是否有一个好的资源可供我阅读与此相关的主题?

我几乎觉得我已经走进了一个死胡同,我找不到(因为我不知道要搜索什么)更多的资源和信息来看看这种方法是否“好”。我计划用 .NET C# 编写此内容,但我认为这些技术和解决方案可以转换为任何语言。所有见解均受到赞赏。

Ben*_*igt 4

您实际上需要四个缓冲区/对象。两个缓冲区/对象由读者拥有,一个由作者拥有,另一个在邮箱中。

读者 - 每次他在新对象上完成一组原子操作时,他都会使用互锁交换将旧对象句柄(指针或索引无关紧要)与邮箱对象句柄交换。然后,他查看新获得的对象,并将序列号与他刚刚读取(并且仍然持有)的对象进行比较,以找出哪个对象更新。

写入者将最新数据的完整副本写入他的对象,然后使用互锁交换将他新写入的对象与邮箱对象交换。

正如您所看到的,写入者可以随时窃取邮箱对象,但绝不能窃取读取者正在使用的邮箱对象,因此读取操作保持原子性。读者可以随时窃取邮箱对象,但绝不能窃取作者正在使用的邮箱对象,因此写入操作保持原子性。

只要互锁交换函数生成正确的内存栅栏(为写入线程中完成的交换释放,为读取线程获取),对象本身可以是任意复杂的。