[数据结构-1]:环形buffer以及读写同步

这篇具有很好参考价值的文章主要介绍了[数据结构-1]:环形buffer以及读写同步。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、什么是环形buffer

二、环形buffer的优点与使用场合

三、环节buffer的读写同步

3.1 基本原理

3.2 代码示例


一、什么是环形buffer

环形缓冲区(Circular Buffer)也被称为环形队列(Circular Queue)或循环缓冲区,是一种数据结构,用于在固定大小的缓冲区中存储和处理数据。

环形缓冲区的特点是首尾相连,即缓冲区的最后一个元素和第一个元素相邻。当缓冲区写满时,新数据可以覆盖旧数据,实现循环利用。

环形缓冲区常见的应用场景是数据流处理,例如音频、视频、网络通信等。它具有以下优点:

  1. 内存利用率高:由于循环利用,不会浪费内存空间。
  2. 读写效率高:读写指针移动固定步长,无需频繁移动指针或进行数据搬移操作。
  3. 简单且高效:相比其他数据结构,环形缓冲区操作简单,性能高。

环形缓冲区的实现通常需要两个指针,表示读和写的位置。读指针用于提取数据,写指针用于写入数据。同时,还需要记录缓冲区的大小和当前数据元素的数量。

通过合理管理读写指针和计数信息,可以实现有效的数据读写和同步,避免数据溢出和读写冲突的问题。

二、环形buffer的优点与使用场合

环形缓冲区(Circular Buffer)具有以下优点和适用场合:

优点:

  1. 内存利用率高:环形缓冲区能够循环利用缓冲区的空间,不会浪费内存。当缓冲区写满时,新的数据可以覆盖旧的数据,避免了频繁地分配和释放内存空间的开销。
  2. 读写效率高:环形缓冲区的读写操作只涉及指针的移动,不需要进行元素的搬移或内存复制,因此读写效率较高。读写指针以固定步长移动,无需频繁更新指针位置,特别适用于实时数据的处理。
  3. 简单且高效:相比其他数据结构,环形缓冲区的实现较为简单,并且具有较高的性能。它不需要复杂的内存管理或链表操作,可以快速读写数据。

使用场合:

  1. 数据流处理:环形缓冲区常用于处理连续的数据流,例如音频、视频、传感器数据等。通过循环利用缓冲区的空间,可实现高效的数据流存储以及快速的读取和处理。
  2. 缓冲数据传输:环形缓冲区可以用于缓冲数据的传输,特别是在生产者和消费者之间的数据传输场景。通过缓冲区,可以平衡生产者和消费者的速度差异,避免数据丢失或阻塞。
  3. 数据采样和循环记录:环形缓冲区适用于需要采样和记录连续数据的应用。例如嵌入式系统中的数据采集、实时监控系统中的历史数据记录等。
  4. 实时任务调度:在实时任务调度中,环形缓冲区可用于存储任务队列。任务按照优先级或时间顺序排列,通过环形缓冲区的循环读取,可以高效地完成实时任务的调度和执行。

总之,环形缓冲区适用于需要循环读写、高效利用内存和处理连续数据的场景,可以提高数据处理的效率和性能。

三、环节buffer的读写同步

3.1 基本原理

在环形buffer中,读和写的同步通常可以通过使用信号量来实现。环形buffer的同步通常需要维护两个指针:read_indexwrite_index,分别表示当前读取到的位置和下一个写入的位置。

读取操作需要获取已经写入的数据,因此需要等待直到有数据可用。这可以通过一个信号量来实现,信号量的值初始化为0,并在写操作完成时加1。每次读取操作首先尝试获取信号量,如果信号量的值<=0,则表示没有数据可用,需要等待。当信号量的值>0时,读取操作将从环形buffer中读取数据,更新read_index指针,并将信号量的值减1。

写入操作也需要同步,因为当buffer写满后就不能再写入新的数据了。写入操作同样需要使用一个信号量,该信号量的值初始化为buffer的大小,并在读取操作完成时加1。每次写入操作首先尝试获取信号量,如果信号量的值<=0,则表示buffer已经写满,需要等待。当信号量的值>0时,写入操作将写入数据到环形buffer中,更新write_index指针,并将信号量的值减1。

这样,通过使用信号量,可以实现环形buffer的读和写的同步,避免了读写的冲突,以及buffer写满和读取空buffer的情况。

3.2 代码示例

以下是一个简单的C++代码示例,展示了如何在环形缓冲区中实现读和写的同步:

#include <iostream>
using namespace std;

const int BUFFER_SIZE = 10;

class CircularBuffer {
private:
  int buffer[BUFFER_SIZE];
  int readIndex;
  int writeIndex;
  int itemCount;

public:
  CircularBuffer() {
    readIndex = 0;
    writeIndex = 0;
    itemCount = 0;
  }

  bool isEmpty() {
    return (itemCount == 0);
  }

  bool isFull() {
    return (itemCount == BUFFER_SIZE);
  }

  void write(int data) {
    if (isFull()) {
      cout << "Buffer is full. Cannot write data." << endl;
      return;
    }

    buffer[writeIndex] = data;
    writeIndex = (writeIndex + 1) % BUFFER_SIZE;
    itemCount++;
    cout << "Data " << data << " has been written to buffer." << endl;
  }

  int read() {
    if (isEmpty()) {
      cout << "Buffer is empty. Cannot read data." << endl;
      return -1; // or any other suitable error value
    }

    int data = buffer[readIndex];
    readIndex = (readIndex + 1) % BUFFER_SIZE;
    itemCount--;
    cout << "Data " << data << " has been read from buffer." << endl;
    return data;
  }
};

int main() {
  CircularBuffer buffer;

  buffer.write(1);
  buffer.write(2);
  buffer.write(3);

  buffer.read();
  buffer.read();
  buffer.read();

  return 0;
}

在上面的示例中,CircularBuffer类表示环形缓冲区,其构造函数初始化了读写指针和计数项。isEmpty()和isFull()函数分别用于检查缓冲区是否为空和是否已满。

write(int data)函数用于将数据写入缓冲区,它先检查缓冲区是否已满,若已满则输出错误信息。若缓冲区未满,则将数据写入缓冲区,同时更新写指针和计数项。

read()函数用于从缓冲区读取数据,它先检查缓冲区是否为空,若为空则输出错误信息,并返回适当的错误值。若缓冲区非空,则读取数据到变量中,同时更新读指针和计数项。

在主函数中创建了一个CircularBuffer对象,并进行了几次写入和读取操作,以展示缓冲区的同步行为。

请注意,该示例是一个简要的示例,实际应用中可能需要更多的同步保证和错误处理机制来处理并发访问和异常情况。

3.3 环形buffer如何通过信号量实现读和写的同步, C++代码示例

要实现环形缓冲区的读和写的同步,你可以使用两个信号量来管理缓冲区的满和空状态。一个信号量用于表示缓冲区是否满,另一个信号量用于表示缓冲区是否空。下面是使用信号量实现读和写同步的C++代码示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;

const int BUFFER_SIZE = 10;

class CircularBuffer {
private:
  int buffer[BUFFER_SIZE];
  int readIndex;
  int writeIndex;
  int itemCount;
  
  mutex mtx;
  condition_variable bufferFull;
  condition_variable bufferEmpty;

public:
  CircularBuffer() {
    readIndex = 0;
    writeIndex = 0;
    itemCount = 0;
  }

  bool isEmpty() {
    return (itemCount == 0);
  }

  bool isFull() {
    return (itemCount == BUFFER_SIZE);
  }

  void write(int data) {
    unique_lock<mutex> lock(mtx);
    bufferFull.wait(lock, [this] { return !isFull(); });

    buffer[writeIndex] = data;
    writeIndex = (writeIndex + 1) % BUFFER_SIZE;
    itemCount++;
    cout << "Data " << data << " has been written to buffer." << endl;

    lock.unlock();
    bufferEmpty.notify_one();
  }

  int read() {
    unique_lock<mutex> lock(mtx);
    bufferEmpty.wait(lock, [this] { return !isEmpty(); });

    int data = buffer[readIndex];
    readIndex = (readIndex + 1) % BUFFER_SIZE;
    itemCount--;
    cout << "Data " << data << " has been read from buffer." << endl;

    lock.unlock();
    bufferFull.notify_one();

    return data;
  }
};

int main() {
  CircularBuffer buffer;

  thread writer([&buffer]() {
    for (int i = 1; i <= 5; i++) {
      buffer.write(i);
      this_thread::sleep_for(chrono::milliseconds(500));
    }
  });

  thread reader([&buffer]() {
    for (int i = 1; i <= 5; i++) {
      buffer.read();
      this_thread::sleep_for(chrono::seconds(1));
    }
  });

  writer.join();
  reader.join();

  return 0;
}

在上述代码中,CircularBuffer类与之前的示例代码类似。在类中添加了互斥锁(mutex)mtx和条件变量(condition variable)bufferFullbufferEmptymtx用于保护共享资源的互斥访问,bufferFullbufferEmpty用于在读写操作中进行等待和唤醒的同步。

write(int data)read()函数中,使用unique_lock来管理互斥锁。在写入操作中,调用bufferFull.wait()来等待缓冲区非满的条件满足,然后执行写入操作。在读取操作中,调用bufferEmpty.wait()来等待缓冲区非空的条件满足,然后执行读取操作。

当写入或读取操作完成后,通过调用bufferEmpty.notify_one()bufferFull.notify_one()来唤醒等待的线程。

在主函数中创建了一个writer线程和一个reader线程,并分别模拟了写入和读取操作。使用chrono库来控制线程的睡眠时间,以展示读写操作的同步行为。

需要注意的是,上述代码仅提供了一个简单的示例,实际应用中可能需要更多的同步保证和错误处理机制,以及对信号量的正确使用和释放。文章来源地址https://www.toymoban.com/news/detail-838431.html

到了这里,关于[数据结构-1]:环形buffer以及读写同步的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构OJ题】环形链表

    原题链接:https://leetcode.cn/problems/linked-list-cycle/description/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 整体思路: 定义 快慢指针fast,slow ,如果 链表确实有环 , fast指针一定会在环内追上slow指针。 即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,

    2024年02月12日
    浏览(40)
  • 【数据结构OJ题】环形链表II

    原题链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/ 如果有小伙伴不了解环形链表,可以先看看这篇文章: https://blog.csdn.net/m0_62531913/article/details/132352203?spm=1001.2014.3001.5502 我们来看下图:  我们根据这个结论就可以做出这道题目了!

    2024年02月12日
    浏览(41)
  • C语言每日一题:13《数据结构》环形链表。

    题目链接: 使用快慢指针利用相对移动的思想: 1,令快指针(fast)速度为2. 2.慢指针(slow)速度为1. 3.以慢指针进入环中开始。 4。假设slow刚刚进入环中fast与它相距N。 如图所示: 1,令快指针(fast)速度为3.M 2.慢指针(slow)速度为1. 3.以慢指针进入环中开始。 4。假设slow刚

    2024年02月14日
    浏览(44)
  • 从零开始学习数据结构—【链表】—【探索环形链的设计之美】

    双向环形链表带哨兵,这个时候的 哨兵 , 可以当头,也可做尾 带哨兵双向循环链表:结构稍微复杂,实现简单。一般用来单独存储数据,实际中使用的链表数据结构都是带头双向链表。另外,这个结构虽然结构复杂,但是使用代码实现后会发现结构会带来很多优势。 双向

    2024年02月22日
    浏览(40)
  • 【STM32 CubeMX】学STM必会的数据结构——环形缓冲区

    在嵌入式系统开发中,经常需要处理数据的缓存和传输,而环形缓冲区是一种常见且有效的数据结构,特别适用于处理实时数据流或者在有限的内存资源下高效地管理数据。在STM32微控制器的开发中,使用CubeMX工具可以方便地配置和生成环形缓冲区的代码,从而加速开发过程

    2024年04月12日
    浏览(29)
  • 【数据结构】LeetCode升级版的环形链表,复制带随机指针的链表

              1、题目说明           2、题目解析           1、题目说明           2、题目解析      1、题目说明 题目链接: 升级版的环形链表  给定一个链表的头节点 head ,返回链表开始入环的第一个节点。  如果链表无环,则返回NULL。 如果链表中有某个节点,可以通

    2024年01月16日
    浏览(57)
  • 数据结构:带环单链表基础OJ练习笔记(leetcode142. 环形链表 II)(leetcode三题大串烧)

    目录 一.前言  二.leetcode160. 相交链表  1.问题描述 2.问题分析与求解 三.leetcode141. 环形链表 1.问题描述 2.代码思路  3.证明分析  下一题会用到的重要小结论: 四.leetcode142. 环形链表 II 1.问题描述 2.问题分析与求解 Judgecycle接口: 方法一: 方法二:  单链表和带环单链表

    2023年04月08日
    浏览(37)
  • 【大数据】LSM树,专为海量数据读写而生的数据结构

    目录 1.什么是LSM树? 2.LSM树的落地实现 LSM树(Log-Structured Merge Tree)是一种专门针对大量写操作做了优化的数据存储结构,尤其适用于现代大规模数据处理系统,如NoSQL数据库(如Cassandra、HBase、RocksDB等)和键值存储。尽管其名称中包含“树”,但它并不直接对应于传统的树状

    2024年04月25日
    浏览(33)
  • Redis缓存读写策略(三种)数据结构(5+3)

    Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。 写 : 先更新 db 然后直接删除 cache 。 读  : 从 cache 中读取数据,读取到就直接返回 cache 中读取不到的话,就从 db 中读取数据返回 再把数据放到 cache 中。 在写数据的过程中,可以先

    2024年02月12日
    浏览(37)
  • MySQL一主一从、配置一主多从结构、数据读写分离

    部署mysql主从同步 配置mysql主从 分为主数据库角色(master)、从数据库服务器角色(slave) 网站服务器连接后存储数据的服务器作为主服务器 自动同步主服务器上的数据 192.168.88.53 做master 启用binlog日志文件 指定server_id 重启服务 用户授权 查看正在使用的binlog日志文件 192.

    2024年01月19日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包