Ring Buffer (circular Buffer)环形缓冲区简介

2023-05-16

关于环形缓冲区的知识,请看这里

http://en.wikipedia.org/wiki/Circular_buffer 

上面这个网址已经介绍得非常详细了。


下面这个网址有 RingBuffer的C代码实现, 其实是一个C的开源库   liblcthw 里实现的。

http://c.learncodethehardway.org/book/ex44.html


开源库 liblcthw的网址为     https://github.com/zedshaw/liblcthw  用C代码实现了一些常用的数据结构,list,map,tree,字符串函数,ring buffer等,学习C语言的人值得看看。



boost 库里也有环形缓冲区的实现, 具体使用的例子如下:

   #include <boost/circular_buffer.hpp>

   int main(int /*argc*/, char* /*argv*/[]) {

      // 创建一个环形缓冲区来存放三个int类型的数据
      boost::circular_buffer<int> cb(3);

      //插入元素
      cb.push_back(1);
      cb.push_back(2);
      cb.push_back(3);

      int a = cb[0];  // a == 1
      int b = cb[1];  // b == 2
      int c = cb[2];  // c == 3

      //环形缓冲区现在已经满了,继续插入元素将会覆盖掉最前面的元素

      cb.push_back(4);  // 用4覆盖了1
      cb.push_back(5);  // 用5覆盖了2

      //环形缓冲区现在包含元素 3, 4 和 5.

      a = cb[0];  // a == 3
      b = cb[1];  // b == 4
      c = cb[2];  // c == 5

      //元素能够被从后面取出也可从前面取出

      cb.pop_back();  // 5 被取出
      cb.pop_front(); // 3 被取出

      int d = cb[0];  // d == 4

      return 0;
   }

上面的例子很简单, 可以让我们对使用方法做一个简单的理解。

有兴趣的同学还可以看看下面的这个例子。

   #include <boost/circular_buffer.hpp>
   #include <numeric>
   #include <assert.h>

   int main(int /*argc*/, char* /*argv*/[])
   {
      //创建一个容量为3的环形缓冲区
      boost::circular_buffer<int> cb(3);

      //插入2个元素进入环形缓冲区
      cb.push_back(1);
      cb.push_back(2);

      // assertions
      assert(cb[0] == 1);
      assert(cb[1] == 2);
      assert(!cb.full());
      assert(cb.size() == 2);
      assert(cb.capacity() == 3);

      //再插入2个元素
      cb.push_back(3);
      cb.push_back(4);

      //计算容器里所有元素之和
      int sum = std::accumulate(cb.begin(), cb.end(), 0);

      //断言
      assert(cb[0] == 2);
      assert(cb[1] == 3);
      assert(cb[2] == 4);
      assert(*cb.begin() == 2);
      assert(cb.front() == 2);
      assert(cb.back() == 4);
      assert(sum == 9);
      assert(cb.full());
      assert(cb.size() == 3);
      assert(cb.capacity() == 3);

      return 0;
   }

还有一种特殊的环形缓冲区叫 边界缓冲区,边界缓冲区是一个典型的生产者消费者模式,生产者生产和存储元素,消费者取出元素,然后进行处理。边界缓冲区有个特别的地方,就是缓冲区满了的时候, 生产者保证不会进行插入元素的工作, 一直要到有空闲空间的时候,才会插入新元素。

边界缓冲区的实现如下所示 :


   #include <boost/circular_buffer.hpp>
   #include <boost/thread/mutex.hpp>
   #include <boost/thread/condition.hpp>
   #include <boost/thread/thread.hpp>
   #include <boost/call_traits.hpp>
   #include <boost/progress.hpp>
   #include <boost/bind.hpp>

   template <class T>
   class bounded_buffer {
   public:

      typedef boost::circular_buffer<T> container_type;
      typedef typename container_type::size_type size_type;
      typedef typename container_type::value_type value_type;
      typedef typename boost::call_traits<value_type>::param_type param_type;

      explicit bounded_buffer(size_type capacity) : m_unread(0), m_container(capacity) {}

      void push_front(boost::call_traits<value_type>::param_type item) {
         // param_type represents the "best" way to pass a parameter of type value_type to a method

         boost::mutex::scoped_lock lock(m_mutex);
         m_not_full.wait(lock, boost::bind(&bounded_buffer<value_type>::is_not_full, this));
         m_container.push_front(item);
         ++m_unread;
         lock.unlock();
         m_not_empty.notify_one();
      }

      void pop_back(value_type* pItem) {
         boost::mutex::scoped_lock lock(m_mutex);
         m_not_empty.wait(lock, boost::bind(&bounded_buffer<value_type>::is_not_empty, this));
         *pItem = m_container[--m_unread];
         lock.unlock();
         m_not_full.notify_one();
      }

   private:
      bounded_buffer(const bounded_buffer&);              // Disabled copy constructor
      bounded_buffer& operator = (const bounded_buffer&); // Disabled assign operator

      bool is_not_empty() const { return m_unread > 0; }
      bool is_not_full() const { return m_unread < m_container.capacity(); }

      size_type m_unread;
      container_type m_container;
      boost::mutex m_mutex;
      boost::condition m_not_empty;
      boost::condition m_not_full;
   };

1.  push_front() 方法被生产者线程调用,目的是插入新元素到buffer中。这个方法会锁住mutex,一直等到有新空间可以插入新元素。 (Mutex锁在等待期间是没有锁住的,只有条件满足的时候才会把锁锁上) 假如在buffer中有一个可用的空间,执行就会继续,该方法就会插入元素进入到环形缓冲区的末尾。 然后未读元素的数量就会增加,然后自动解锁。 (在例子中,Mutex锁解锁是会抛出一个异常,锁会在scoped_lock对象的析构函数中自动被打开). 最后,这个方法会通知其中的一个消费者线程,告诉他们,有一个新的元素插入了缓冲区。

2. pop_back() 方法被消费者线程调用,目的是为了从buffer中读取下一个元素。这个方法会锁住Mutex然后等待,直到有一个未读的元素进入缓冲区。 假如至少有一个未读元素的时候,这个方法就会减少未读元素的数量,然后从circular_buffer中读取一个未读元素。然后就解锁Mutex,并通知等待中的一个生产者线程,告诉它又新的空间可以插入新元素了。

3. 这个 pop_back() 方法移除元素,元素仍然留在 circular_buffer 里,这样的话,当circular_buffer满的时候它就会被生产者线程用一个新的元素替代。这个技术比移除一个元素更有效率。


下面的网址是一个环形缓冲区的 C++ 实现,可以用来处理二进制数据,改天有空了翻译一下,方便大家阅读。

http://www.asawicki.info/news_1468_circular_buffer_of_raw_binary_data_in_c.html

Circular Buffer of Raw Binary Data in C++

Circular Buffer, Cyclic Buffer or Ring Buffer is a data structure that effectively manages a queue of some items. Items can be added at the back and removed from the front. It has limited capacity because it is based on preallocated array. Functionality is implemented using two pointers or indices - pointing to the first and past the last valid element. The Begin pointer is incremented whenever an item is popped from the front so that it "chases" the End pointer, which is incremented whenever a new item is pushed to the back. They can both wrap around the size of the array. Both operations are done very effectively - in constant time O(1) and no reallocations are needed. This makes circular buffers perfect solution for queues of some data streams, like video or audio.

It's not very sophisticated data structure, but there is one problem. Sample codes of circular buffers you can find on the Internet, just like for many other data structures, operate usually on a single object of some user-defined type. What if we need a buffer for raw binary data, stored as array of bytes? We can treat single bytes as data items, but enqueueing and dequeueing single bytes with separate function calls would not be efficient. We can, on the other hand, define some block of data (like 4096 bytes) as the type of item, but this limits us to operating on on such block at a time.

Best solution would be to write an implementation that operates on binary data in form of (const char *bytes, size_t byte_count) and allows writing and reading arbitrary amount of data in a single call, just like functions for writing and reading files do. The only problem that arises in such code is that sometimes the block of data you want to write to or read from the buffer is not in a continuous region of memory, but wraps around to the beginning of the array so we have to process it on two parts - first at the end of the array and the second at the beginning.

Here is my C++ implementation of a circular buffer for raw binary data:


#include <algorithm> // for std::min

class CircularBuffer
{
public:
  CircularBuffer(size_t capacity);
  ~CircularBuffer();

  size_t size() const { return size_; }
  size_t capacity() const { return capacity_; }
  // Return number of bytes written.
  size_t write(const char *data, size_t bytes);
  // Return number of bytes read.
  size_t read(char *data, size_t bytes);

private:
  size_t beg_index_, end_index_, size_, capacity_;
  char *data_;
};

CircularBuffer::CircularBuffer(size_t capacity)
  : beg_index_(0)
  , end_index_(0)
  , size_(0)
  , capacity_(capacity)
{
  data_ = new char[capacity];
}

CircularBuffer::~CircularBuffer()
{
  delete [] data_;
}

size_t CircularBuffer::write(const char *data, size_t bytes)
{
  if (bytes == 0) return 0;

  size_t capacity = capacity_;
  size_t bytes_to_write = std::min(bytes, capacity - size_);

  // Write in a single step
  if (bytes_to_write <= capacity - end_index_)
  {
    memcpy(data_ + end_index_, data, bytes_to_write);
    end_index_ += bytes_to_write;
    if (end_index_ == capacity) end_index_ = 0;
  }
  // Write in two steps
  else
  {
    size_t size_1 = capacity - end_index_;
    memcpy(data_ + end_index_, data, size_1);
    size_t size_2 = bytes_to_write - size_1;
    memcpy(data_, data + size_1, size_2);
    end_index_ = size_2;
  }

  size_ += bytes_to_write;
  return bytes_to_write;
}

size_t CircularBuffer::read(char *data, size_t bytes)
{
  if (bytes == 0) return 0;

  size_t capacity = capacity_;
  size_t bytes_to_read = std::min(bytes, size_);

  // Read in a single step
  if (bytes_to_read <= capacity - beg_index_)
  {
    memcpy(data, data_ + beg_index_, bytes_to_read);
    beg_index_ += bytes_to_read;
    if (beg_index_ == capacity) beg_index_ = 0;
  }
  // Read in two steps
  else
  {
    size_t size_1 = capacity - beg_index_;
    memcpy(data, data_ + beg_index_, size_1);
    size_t size_2 = bytes_to_read - size_1;
    memcpy(data + size_1, data_, size_2);
    beg_index_ = size_2;
  }

  size_ -= bytes_to_read;
  return bytes_to_read;
}  

Similar phenomenon can be observed in API of the FMOD sound library. Just like graphical textures in DirectX, sound samples in FMOD can also be "locked" to get pointer to a raw memory we can read or fill. But DirectX textures lie in the continuous memory region, so we get a single pointer. The only difficult thing in understanding locking textures is the concept of "stride", which can be greater than the width of a single row. Here in FMOD the Sound::lock() method returns two pointers and two lengths, probably because the locked region can wrap over end of internally used circular buffer like the one shown above.




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ring Buffer (circular Buffer)环形缓冲区简介 的相关文章

  • vs2010开发qt程序debug正常,release出错

    在debug模式下 xff0c 配置的动态链接库是qtmaind lib QtGuid4 lib QtCored4 lib 这些链接库 xff0c 在release模式下是不适用的 xff0c 进入到qt的目录下 xff0c 发现了有一些不
  • cout与wcout

    一直以来只知道有cout用来输出 xff0c 今天用cout输出wchar时出现问题了 xff0c 输出结果是一段地址 xff0c 才发现了wcout的存在 使用wcout输出中文时 xff0c 又出现问题 xff0c 中文输出不了 xff
  • 主引导记录(MBR)信息分析与获取

    前段时间在安装黑苹果时 xff0c 发现一个问题 xff0c 电脑在启动时 xff0c 会找激活分区 xff0c 如果没有找到 xff0c 那就启动不起来 那能否写个小程序读取一下MBR信息 xff0c 把激活分区换成其它 xff0c 搞点
  • Duilib登录窗口

    先上效果图 xff08 自己感觉还不错 xff09 xff1a 功能不完善 xff0c 一是为了熟悉xml的写法 xff0c 手写 xff0c 不建议使用编辑器 xff0c 二了为了理顺程序的流程 xff0c 加入了部分注释 xml文件 l
  • Gitee Pages Pro + Hexo自定义域名

    前景摘要 xff1a 最近 xff0c 本菜鸡打算把hexo的博客站点搬到gitee xff0c 毕竟gitee pages pro有一个月的免费自定义域名的机会 xff01 xff01 其实最主要的原因还是coding pages的延迟有
  • 人脸识别流程

    一 人脸识别技术流程 xff1a 1 人脸图像采集及检测 在人脸检测算法中 xff0c 有模板匹配模型 Adaboost模型等 xff0c 其中Adaboost模型在速度和精度的综合性能上表现最好 该算法特点就是训练慢 xff0c 检测快
  • Ubuntu 18.04 系统自带浏览器闪出问题解决

    首先解释一下闪的是什么 xff1f 他是gnome 网络管理器自带的网络链接检查 xff0c 我们会经常遇到它闪以下然后就退出的问题 xff0c 这可能与我们修改主题有关 xff0c 有时还偶尔会看到这个系统自带浏览器没有闪退 xff0c
  • HTML5中 audio标签的样式修改

    由于html5的流行 xff0c 现在移动端大多数的需求都可以使用audio来播放音频 xff0c 但您可能只是需要很简单的播放 停止效果 xff0c 但不同的浏览器上的audio样式却不尽人意 xff0c 那么要怎么改变这个样式呢 xff
  • VC获取当前电脑所有网络连接名字

    最近因为项目有需要获取本机的所有存在的网络连接名称 在网上也找了资料 有好几种方法 不过就只有一种是能够达到我想要的要求 写下来给大家参考下 第一种方法 遍历注册表来获取 void fastcall MyGetLanAdapterName
  • 【Android Jetpack系列】一、ViewBinding的使用

    关于本系列的说明 作为学习Jetpack的系列文章 可能会更新得很慢 本系列文或者应该称之为学习笔记 观看本文的同学 应该已经有具备开发简单Android App的能力了 若是零基础 那么阅读本文可能有些难懂 我只能尽量简单解释 本文所用开
  • Maven中pom配置(springmvc)

    Maven 生成目录结构 在需要创建目录的位置 xff0c 命令行创建 web 目录结构 mvn archetype generate DgroupId 61 xxx1 DartifactId 61 xxx2 DarchetypeArtif
  • 使用eclipse 4.3 经常出现卡死、无响应情况的解决方法

    最近在使用 eclipse 4 3 开发的时候 xff0c 经常出现卡死 无响应 情况 在网上搜索了一下之后发现 xff0c 发现网上还是有解决方法的 于是以记之 xff01 一 首先 xff0c 我们修改下eclipse的内存配置文件 l
  • Android学习之 移动应用<App>微信支付集成小结

    微信支付现在主要集成在 xff1a 1 移动应用开发 2 网站应用开发 3 公众账号开发 本篇主要针对移动应用App集成微信支付 xff0c 实际项目坑点分享 xff01 一 既予之 与共之 xff1a 平台资源 1 微信开放平台 xff1
  • Android学习之 主项目合并Library子项目中的Manifest

    一 项目背景 xff1a 项目XX是一个按模块化规则来进行开发的 xff0c 包含主模块A 子模块B 子模块C 子模块D xff0c 其中子模块B C D都是Library项目 xff0c 并且都包含有自己的Actity等资源文件 Andr
  • Android学习之 Manifest中meta-data扩展元素数据的配置与获取

    在AndroidManifest xml清单文件中 我们有时会看到如下类似的 lt meta data gt 元素开始的配置内容 xff1a lt meta data android name 61 34 com google androi
  • 工具使用之 adbWireless无线调试Android应用

    今天巧遇这个工具 xff1a adbwireless apk xff0c 于是乎 试爽了一把 xff0c 果然觉得是个不错的工具 可谓是相见恨晚 可以帮助Android开发的同事们实现手机无线调试应用程序 对 xff01 你没有听错 如果你
  • Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒

    在Android手机上 xff0c 如QQ 微信当有未读消息的时候 我们可以看到在应用的启动图标的右上角会有一个红色圈圈 且圈圈里会动态显示未读消息的数目 xff0c 如下图显示 xff1a 那么该功能是怎么实现的呢 xff1f 在万能的互
  • 工具使用之Android Studio快捷键-mac版

    最近给自己添置了一台mac 也算是完成了多年前的一个小愿望 做为Android开发者的我于是搭载了Android Studio 1 1正式版做为了我的安卓开发工具 在window上eclipse我可以畅快的玩耍 xff0c idea和as也
  • Android学习之 Scroller的介绍与使用

    类概述 Android里Scroller类是为了实现View平滑滚动的一个Helper类 通常在自定义的View时使用 xff0c 在View中定义一个私有成员mScroller 61 new Scroller context 设置mScr
  • Android 从外部网页拉起跳转到App

    业务场景 当需要从外部第三方网页中通过点击某个链接或按钮启动App应用程序 实现 新建demo工程 xff0c 并实现一个Activity xff0c 用来接收从外部跳转传入的信息 代码如下 xff1a span class hljs ke

随机推荐