环形缓冲RingBuffer和无锁

2023-10-10 03:52

本文主要是介绍环形缓冲RingBuffer和无锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

环形缓冲

环形缓冲区,也被称为循环缓冲区或者环形队列,是一种数据结构类型,它在内存中形成一个环形的存储空间。环形缓冲区的特点是其终点和起点是相连的,形成一个环状结构。这种数据结构在处理流数据和实现数据缓存等场景中具有广泛的应用。

环形缓冲区的主要作用是存储和管理数据。它可以存储一定数量的数据,并且在数据存储满后,新的数据会覆盖最早的数据,从而实现了一种“先进先出”(FIFO)的数据管理方式。这种数据结构的优点是可以高效地利用有限的缓存空间,避免内存碎片,并且可以在多线程环境中实现数据的同步处理。熟悉Linux内核的读者应该对KFIFO并不陌生,详细的内容可以参考此篇文章链接: KFIFO.

typedef struct
{uint8_t* buff; //缓冲区uint16_t size; //缓冲区大小  uint16_t* read_ptr; //读指针     uint16_t* write_ptr; //写指针 
} RingBuffer;

缓冲区是满、或是空,都有可能出现读指针与写指针指向同一位置,有多种策略用于检测缓冲区是满、或是空。常用的做法是总是保持一个存储单元为空,缓冲区中总是有一个存储单元保持未使用状态。缓冲区最多存入 size-1个数据。如果读写指针指向同一位置,则缓冲区为空。如果写指针位于读指针的相邻后一个位置,则缓冲区为满。这种策略的优点是简单、鲁棒;缺点是语义上实际可存数据量与缓冲区容量不一致,测试缓冲区是否满需要做取余数计算。

无锁

在多线程环境中,如果有多个写线程,那必然是要加锁的。但是,对于单生产单消费模式(即一个线程只读一个线程只写)则无需加锁。

对于单线程读单线程写一个变量,一般来说是要加锁的,以避免写变量的非原子性操作,导致读线程读出中间态的数据。

对于简单数据类型(int、bool等)的多线程读写安全性跟CPU架构相关。在x86上,对1字节byte/2字节word/4字节int类型的读写操作都是原子性的。即1个int不会有中间状态,若它原始值是0,往其写入0x12345678,则另一个线程绝对不会读到0x12000000或是0x00005678的值。在64位x86上,size_t(8字节)也是原子操作(多核,volatile)。

对于变量需要进行一些计算的情况,如a = a + 1,这个操作毫无疑问肯定不是原子操作。这个语句简单的转换为类似机器语言,第一步,把a读取到一个寄存器中,第二步将寄存器中的值加1,第三步将寄存器中的新值写入a中。线程B要写a,做了三步操作中任何一步结束,线程A去读a,a仍是一个有效值,只不过a并不一定是一个最新的值。如果线程A并不关心a是不是最新的值,只关心a是否是一个合法值就可以了,至于最新值可以通过循环去不断刷新。因为有了字节对齐,一个读周期或是一个写周期仅需要一个总线周期,在这个总线周期内就把这个整型变量给处理了, 一个总线周期结束前CPU不会被抢占,就是中断发生也不会导致一个总线周期执行一半时CPU被抢占(CPU是在现行指令结束后响应中断,即运行到最后一个指令周期中的最后一个总线周期中的最后一个T状态时CPU才采样INTR线来查看是否有中断请求)。

这篇关于环形缓冲RingBuffer和无锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/177757

相关文章

环形定时任务 原理

业务背景 在稍微复杂点业务系统中,不可避免会碰到做定时任务的需求,比如淘宝的交易超时自动关闭订单、超时自动确认收货等等。对于一些定时作业比较多的系统,通常都会搭建专门的调度平台来管理,通过创建定时器来周期性执行任务。如刚才所说的场景,我们可以给订单创建一个专门的任务来处理交易状态,每秒轮询一次订单表,找出那些符合超时条件的订单然后标记状态。这是最简单粗暴的做法,但明显也很low,自己都下不去手写

Linux多线程——POSIX信号量与环形队列版本之生产消费模型

文章目录 POSIX信号量POSIX的操作初始化销毁等待信号量(申请资源)发布信号量(放下资源) 环形队列之生产消费模型 POSIX信号量 POSIX信号量和System V信号量是不同的标准 但是实现的功能是一样的,都是为了解决同步的问题 我们说信号量指的就是资源的数量 在生产者与消费者模型里面,生产者与消费者所认为的资源是不同的 生产者认为空间是资源,因为每次都要

工作集、granule、缓冲区、缓冲池概念及关系?

工作集、granule、缓冲区、缓冲池概念及关系? granule:为了让内存在db_chache_size和shared_pool_size之间高效的移动,oracle在9i重构SGA,使用固定大小的内存块即为granule。这个参数就是为什么当你分配给shared pool值的时候,为什么有时候比你分配的值要大一点,但是granule的整数倍。 缓冲区:内存存放数据的地方,类似于数

NYOJ 737 石子合并(一)(环形)

OJ题目:http://acm.nyist.net/JudgeOnline/problem.php?pid=737 参考资料:http://wenku.baidu.com/view/b4dbe923482fb4daa58d4b84.html (感觉解释的很好) 描述     有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次

图形API学习工程(8):使用顶点索引缓冲

工程GIT地址:https://gitee.com/yaksue/yaksue-graphics 目标 在《图形API学习工程(5):图形管线&顶点缓冲》中,实现了渲染出一个三角形。 他有三个顶点。但是考虑图形变得复杂些的情况,就假如是一个四边形吧,那就需要分解为两个三角形来渲染了,而每个三角形需要三个顶点,也就是说,共需要6个顶点数据。 然而,如上图所示,实际上顶点是有重复的:0和3重复

【大数据Java基础-JAVA IO 4】JAVA IO流 (四) 缓冲流的使用

packageatguigu.senior.day11.java;importorg.junit.Test;import java.io.*;/*** 处理流之一:缓冲流的使用 1.缓冲流: BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 2.作用:提供流的读取、写入的速度 提高读写

echarts环形图

let dataValue=[{value: 30,name: '桥梁',percent: 0.25,color: 'rgba(248,95,94,1)',radius: ['75%', '80%'],center: ['22%', '50%'],},{value: 15,name: '隧道',percent: 0.25,color: 'rgba(243,185,71,1)',radius:

echarts多个环形图

echarts图表集  var dataValue = [{name:'今日待分配方量',value:49}, {name:'今日已分配方量',value:602}, {name:'今日完成方量',value:1037}]var piedata1 = [{name: '1#拌和机',value: 20},{name: '2#拌和机',value: 22},{name: '3#拌和机 ',va

【Canvas与纹饰】环形小蜜蜂纹饰

【成图】 【代码】 <!DOCTYPE html><html lang="utf-8"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><head><title>环形小蜜蜂纹饰</title><style type="text/css">.centerlize{margin:0 auto;

阅读笔记(五)多线程无锁的C++实现《Lock-Free Data Structures》

1. 前言   本文介绍使用C++实现多线程中无锁算法的实现和优化过程。 2. 无锁&CAS   在多线程程序中,加锁是一种必要的手段,由于保证数据操作的正确性(原子性)。但是这也在很多时候带来了性能的极度下降,因为所有共享数据的操作均需要加锁,有些时候会严重影响性能,比如当读键盘或者一些较慢的I/O操作时,锁会延误了其他线程的操作。更糟糕的是,不当操作可能会带来死锁。   首先介绍最经典