没有优先级倒置的环形缓冲区

时间:2011-04-21 15:51:35

标签: 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 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()中的数学运算可能是有可能的。或者更新结束不是原子的,引入了一个错误。

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

我或许可以使用原子操作来解决问题,但我不知道有一个很好的跨平台库提供这些。

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

3 个答案:

答案 0 :(得分:4)

只要您遵守这些准则,您所拥有的就应该没问题:

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

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

  • 您认为count可能会在您获得值时过期。在阅读主题中,count将返回您可以依赖的最小计数;写作线程count将返回最大计数,真实计数可能会更低。

答案 1 :(得分:2)

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

即将推出的C ++标准将原子操作添加到标准库中,因此它们将来可用,但大多数实现都不支持它们。

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

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

基本上,你会限制写''访问高优先级线程,只允许低优先级线程读取。这样,您可以避免所有锁定,并且高优先级线程不必担心等待较低线程完成写入,从而使高优先级线程成为真正的高优先级。

答案 2 :(得分:1)

boost::interprocessboost/interprocess/detail/atomic.hpp

中提供跨平台原子功能