C++ 无优先级反转的环形缓冲区

C++ 无优先级反转的环形缓冲区,c++,concurrency,circular-buffer,C++,Concurrency,Circular Buffer,我有一个高优先级进程,需要将数据传递给低优先级进程。我编写了一个基本的环形缓冲区来处理数据传递: class RingBuffer { public: RingBuffer(int size); ~RingBuffer(); int count() {return (size + end - start) % size;} void write(char *data, int bytes) { // some work that uses on

我有一个高优先级进程,需要将数据传递给低优先级进程。我编写了一个基本的环形缓冲区来处理数据传递:

class RingBuffer {
  public:
    RingBuffer(int size);
    ~RingBuffer();

    int count() {return (size + end - start) % size;}

    void write(char *data, int bytes) {
      // some work that uses only buffer and end
      end = (end + bytes) % size;
    }

    void read(char *data, int bytes) {
      // some work that uses only buffer and start
      start = (start + bytes) % size;
    }

  private:
    char *buffer;
    const int size;
    int start, end;
};
问题出在这里。假设低优先级进程有一个oracle,它确切地告诉它需要读取多少数据,这样就永远不需要调用
count()
。然后(除非我遗漏了什么),就没有并发问题了。但是,只要低优先级线程需要调用
count()
(高优先级线程可能也要调用它以检查缓冲区是否已满),count()中的数学运算或结束更新就可能不是原子的,从而引入错误

我可以在访问开始和结束时放置一个互斥锁,但如果高优先级线程必须等待低优先级线程获取的锁,则会导致优先级反转

我可能能够使用原子操作解决一些问题,但我不知道有一个很好的跨平台库提供这些功能


是否有一种标准的环形缓冲区设计可以避免这些问题?

只要您遵守以下准则,您所拥有的应该是正常的:

  • 只有一个线程可以执行写操作
  • 只有一个线程可以执行读取
  • start
    end
    的更新和访问是原子的。这可能是自动的,例如Microsoft声明:
简单的读写操作 正确对齐的32位变量是 原子操作。换句话说,你 不会只吃一份 变量的更新时间;所有位都是 以原子的方式更新

  • 您可以考虑这样一个事实,
    count
    即使在您获得值时也可能已过期。在读取线程中,
    count
    将返回您可以依赖的最小计数;对于写入线程,
    count
    将返回最大计数,真实计数可能更低

    • Boost提供了一个循环缓冲区,但它不是线程安全的。不幸的是,我不知道有任何实现是这样的

      即将到来的C++标准将原子操作添加到标准库中,因此它们将来可用,但大多数实现还不支持它们。 我看不到任何跨平台的解决方案可以在两个线程都写入时保持

      count
      正常,除非实现锁定

      通常,您可能会使用消息传递系统,强制低优先级线程请求高优先级线程进行更新或类似操作。例如,如果低优先级线程消耗15个字节,它应该要求高优先级线程将计数减少15


      本质上,您将限制对高优先级线程的“写”访问,而只允许低优先级线程读取。这样,您可以避免所有锁定,高优先级线程不必担心等待较低线程完成写入,使高优先级线程真正具有高优先级。

      boost::interprocess
      boost/interprocess/detail/atomic中提供跨平台原子函数。hpp

      编译器是否存在将更新拆分为非原子步骤序列的风险?即,将write()的最后一行转换为
      end+=字节;结束%=大小?编译器可以非原子地计算RHS,根据您的平台,甚至可能非原子地存储int(即,在8位平台上使用两次8位写入来存储16位int)。@user168715,如果您绝对不相信编译器会做正确的事,创建一个
      volatile
      临时变量来保存计算,然后复制它以进行更新。您对所使用的平台有任何限制吗?@Peter让我们假设x86(其中IIRC 32位写入对齐地址是原子的),但越是可移植越好。下面是一个队列,它的运行基本上是无等待的