惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MyScale Blog
MyScale Blog
F
Fortinet All Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
V
Visual Studio Blog
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
L
LangChain Blog
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
IT之家
IT之家
P
Proofpoint News Feed
博客园_首页
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog

博客园 - WmW

C#学习牛顿迭代法开方 mysql将一个表中指定时间之后的新数据导入到另一个表中 使用具体时间和DateTime.Now运算时需要注意DateTime.Now的毫秒数 简单搭建一个 ASP.NET Core Web API + Mysql + SqlSugar demo项目 C# 学习研究CRC校验 C# 学习逆变和协变 C# 标准的Dispose模式 C# 返回Task或者Task<T>的方法中如果没有异步方法,就没必要使用async修饰 关于字节序的概念加深 简单接触BCD码,以及使用C#简单实现BCD转换 C# ReadOnlySequence和ReadOnlySequenceSegment简单使用 C# 非常简单的文字转语音实现 C# 输出年龄和属相列表 C# 封装了一个用来对参数值进行范围限制的泛型方法 C# 为WindowsDefender防火墙已经存在的入站规则添加IP地址 C# 将Framework4.8控制台程序注册为windows服务 C# async void 方法中使用await时外部不会等待 C# Channel学习 C# 使用字符串分割字符串 C# 一个简单的连续心率血氧压缩算法 C# 将日期时间按照ISO 8601标准转成字符串 Dapper传递参数对象时,只支持属性,无法解析字段(出现Parameter '?id' must be defined)
C# 基于ReadOnlySequence和ReadOnlySequenceSegment的简单封装
WmW · 2025-06-10 · via 博客园 - WmW

最近在研究ReadOnlySequence,这是C#的一个高性能组件,能够将非连续内存当作连续内存使用,减少内存拷贝,看上去很不错,

为了加深对其理解,特自己封装了一个既能不断附加新的内存段,又能释放已读内存段,还能把未读内存段当作一个整体使用的类,

代码比较简单,只用来学习理解,不建议直接使用,如果实际业务有需求,可以试试System.IO.Pipelines.Pipe,这个甚至实现了内存的GC管理,还是线程安全的

    using System.Buffers;
    /// <summary>
    /// 一个简单的可以直接创建ReadOnlySequence的类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ReadOnlySequenceFactory<T> {
        /// <summary>
        /// 第一个段
        /// </summary>
        MemorySegment? first;
        /// <summary>
        /// 最后一个段
        /// </summary>
        MemorySegment? last;
        /// <summary>
        /// 数据开始索引
        /// </summary>
        int startIndex;
        /// <summary>
        /// 总长度
        /// </summary>
        int totalLength;
        /// <summary>
        /// 序列链中剩余数据长度
        /// </summary>
        public int Length => totalLength - startIndex;
        /// <summary>
        /// 向序列中附加新的内存段
        /// </summary>
        /// <param name="memory">要附加的内存段</param>
        public void Append(ReadOnlyMemory<T> memory) {
            if (first == null) {
                first = new MemorySegment(memory);
                last = first;
            } else {
                last = last?.Append(memory);
            }
            totalLength += memory.Length;
        }
        /// <summary>
        /// 前进count个长度,将其标记为已读,如果首序列全部已读,就会自动释放
        /// </summary>
        /// <param name="count">前进的长度</param>
        public void Advance(int count) {
            if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "count不能为负数");
            if (count == 0) return;
            if (count > Length) throw new ArgumentOutOfRangeException(nameof(count), "前进长度超出剩余数据长度");
            startIndex += count; //索引前进count个
            while (true) {
                if (first == null) { //序列为空时
                    startIndex = 0;
                    return;
                }
                if (startIndex < first.Memory.Length) { //索引未超出首序列,首序列还有数据
                    return;
                }
                if (first.Next == null) { //序列读完时
                    first = null;
                    last = null;
                    startIndex = 0;
                    return;
                }
                //此处开始索引超出了首序列的长度并且还有后续序列,就释放首序列
                startIndex -= first!.Memory.Length; //减去首序列长度得到下一个序列的开始索引
                totalLength -= first.Memory.Length; //释放首序列长度
                first = first.Next as MemorySegment; //指向新的首序列
            }
        }
        /// <summary>
        /// 基于当前序列链创建一个ReadOnlySequence将其当作一段连续的内存段
        /// </summary>
        /// <returns>逻辑上连续的内存段</returns>
        public ReadOnlySequence<T> Create() {
            if (first == null || last == null) {
                return ReadOnlySequence<T>.Empty;
            }
            if (first == last) { //如果序列中只有一个段,直接使用更高效的构造函数
                return new ReadOnlySequence<T>(first.Memory[startIndex..]);
            }
            return new ReadOnlySequence<T>(first, startIndex, last, last.Memory.Length);
        }
        /// <summary>
        /// ReadOnlySequenceSegment是抽象类,需要具体实现
        /// </summary>
        class MemorySegment : ReadOnlySequenceSegment<T> {
            /// <summary>
            /// 创建一个序列段
            /// </summary>
            /// <param name="memory">一个连续内存</param>
            internal MemorySegment(ReadOnlyMemory<T> memory) {
                Memory = memory;
            }
            /// <summary>
            /// 为当前序列段附加一个后续序列段
            /// </summary>
            /// <param name="memory">一段连续的内存</param>
            /// <returns></returns>
            internal MemorySegment Append(ReadOnlyMemory<T> memory) {
                var segment = new MemorySegment(memory) {
                    RunningIndex = RunningIndex + Memory.Length //表示的是逻辑意义上的索引,在释放头节点时,一般不用更新其子节点以及后续节点的RunningIndex
                };
                Next = segment;
                return segment;
            }
        }
    }

测试代码

        public void Test() {
            ReadOnlySequenceFactory<byte> factory = new ReadOnlySequenceFactory<byte>();
            factory.Append(new byte[] { 0, 1, 2, 3, 4 }); //填充数据
            factory.Append(new byte[] { 5, 6, 7, 8, 9 });
            Console.WriteLine(factory.Length); //输出工厂里得可读数据长度
            Output("初始化", factory.Create());
            factory.Advance(6);
            Console.WriteLine(factory.Length);
            var sequence1 = factory.Create();
            factory.Append(new byte[] { 10, 11, 12, 13, 14 }); //新附加的不会影响之前的ReadOnlySequence
            Output("前进6个后", sequence1);
            Console.WriteLine(factory.Length);
            var sequence2 = factory.Create(); //每次变更后,都需要重新生成ReadOnlySequence
            Output("附加5个后", sequence2);
        }
        /// <summary>
        /// 输出相关信息
        /// </summary>
        static void Output(string flag, ReadOnlySequence<byte> sequence) {
            byte[] temp = new byte[sequence.Length];
            sequence.CopyTo(temp);
            Console.WriteLine($"{flag},长度:{sequence.Length},数据:{string.Join(",", temp)}");
        }