寻找一种无锁的RT安全单读写器单写器结构

时间:2010-02-25 15:07:30

标签: multithreading real-time lock-free

我正在寻找符合这些要求的无锁设计

  • 单一作家写入结构,单读者从此结构中读取(此结构已存在并且可以安全地同时读/写)
  • 但是在某个时候,结构需要由作者更改,然后初始化,切换和写入新结构(相同类型但具有新内容)
  • 并且在读者下次读取时,它切换到这个新结构(如果编写器多次切换到新的无锁结构,读取器会丢弃这些结构,忽略它们的数据)。
  • 必须重复使用这些结构,即在写入/读取/切换操作期间不允许堆内存分配/释放,用于 RT用途

我目前已经实现了一个包含这些结构的多个实例的ringbuffer;但是这个实现受到以下事实的影响:当编写器使用了环形缓冲区中存在的所有结构时,没有更多的地方可以从结构中改变......但是,其余的环形缓冲区包含一些不必读取的数据由读者但不能被作者重复使用。因此,环形缓冲区不适合这个目的。

任何想法(名称或伪实现)的无锁设计?感谢您考虑过这个问题。

2 个答案:

答案 0 :(得分:0)

你走在正确的轨道上。

在线程/进程/处理器之间锁定固定消息的自由通信 alt text http://img59.imageshack.us/img59/5273/basicq.gif

如果有一个生产者和一个消费者,

固定大小环形缓冲区可用于线程,进程或处理器之间的无锁通信。一些检查要执行:

head 变量只由生产者编写(写作后作为原子动作)

tail 变量仅由消费者编写(作为阅读后的原子动作)

陷阱:引入大小变量或缓冲区满/空标志;这些通常由生产者和消费者编写,因此会给你一个问题。

我通常会为这个purpoee使用环形缓冲区。我学到的最重要的一课是,环形缓冲区永远不会包含多个元素。这样, head tail 变量由生产者和消费者编写。

大/可变大小块的扩展 要在实时环境中使用缓冲区,您可以使用内存池(通常在实时操作系统中以优化形式提供)或从使用中分离分配。我相信后者符合这个问题。

extended queue http://img16.imageshack.us/img16/8725/extq.gif

如果你需要交换大块,我建议使用带缓冲区块的池,并使用队列传递指向缓冲区的指针。因此,请使用带缓冲区指针的第3个队列。这样,分配可以在应用程序(后台)中完成,而实时部分可以访问可变数量的内存。

<强>应用

while (blockQueue.full != true)
{
    buf = allocate block of memory from heap or buffer pool
    msg = { .... , buf };
    blockQueue.Put(msg)
}

Producer:
   pBuf = blockQueue.Get()
   pQueue.Put()

Consumer
   if (pQueue.Empty == false)
   {
      msg=pQueue.Get()
      // use info in msg, with buf pointer
      // optionally indicate that buf is no longer used
   }

答案 1 :(得分:0)

这是一个。关键是有三个缓冲区,读取器保留它正在读取的缓冲区。编写器写入另外两个缓冲区之一。碰撞的风险很小。此外,这扩大了。只需使你的成员数组的元素长于读者数量和作者数量。

class RingBuffer
{
  RingBuffer():lastFullWrite(0)
  { 
    //Initialize the elements of dataBeingRead to false
    for(unsigned int i=0; i<DATA_COUNT; i++)
    {
      dataBeingRead[i] = false;
    } 
  }

  Data read()
  {
    // You may want to check to make sure write has been called once here
    // to prevent read from grabbing junk data. Else, initialize the elements
    // of dataArray to something valid.
    unsigned int indexToRead = lastFullWriteIndex;
    Data dataCopy;
    dataBeingRead[indexToRead] = true;
    dataCopy = dataArray[indexToRead];
    dataBeingRead[indexToRead] = false;
    return dataCopy;
  }

  void write( const Data& dataArg )
  {
    unsigned int writeIndex(0);

    //Search for an unused piece of data.
    // It's O(n), but plenty fast enough for small arrays.
    while( true == dataBeingRead[writeIndex] && writeIndex < DATA_COUNT )
    {
      writeIndex++;
    }  

    dataArray[writeIndex] = dataArg;

    lastFullWrite = &dataArray[writeIndex];
  }

private:
  static const unsigned int DATA_COUNT;
  unsigned int lastFullWrite;
  Data dataArray[DATA_COUNT];
  bool dataBeingRead[DATA_COUNT];
};

注意:在这里写的方式,有两个副本来读取您的数据。如果通过引用参数将数据从读取函数中传出,则可以将其减少到一个副本。