没有优先级反转的环形缓冲区

ring buffer without priority inversion

本文关键字:缓冲区 优先级      更新时间:2023-10-16

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

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()中的数学运算或到结束的更新就有可能不是原子性的,从而引入错误。

我可以在开始和结束的访问周围放一个互斥锁,但是如果高优先级线程必须等待低优先级线程获得锁,那么这会导致优先级反转。

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

是否有一个标准的环缓冲区设计来避免这些问题?

只要你遵守这些准则,你所拥有的应该是可以的:

    只有一个线程可以写。只有一个线程可以做读操作。
  • startend的更新和访问是原子的。这可能是自动的,例如Microsoft声明:

简单的读写正确对齐的32位变量是原子操作。换句话说,就是你最后不会只吃一份吗更新的变量的;所有比特都是以原子方式更新。

  • 你允许count可能过时的事实,即使你得到的值。在读取线程中,count将返回您可以依赖的最小计数;对于写线程count将返回最大计数,而真实计数可能更低。

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

即将发布的c++标准将原子操作添加到标准库中,因此它们将在未来可用,但大多数实现尚未支持它们。

我没有看到任何跨平台的解决方案来保持count正常,而两个线程都写它,除非你实现锁定。

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

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

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