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

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
罗磊的独立博客
腾讯CDC
云风的 BLOG
云风的 BLOG
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
U
Unit 42
I
InfoQ
D
DataBreaches.Net
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
美团技术团队
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
GbyAI
GbyAI
S
SegmentFault 最新的问题

博客园 - 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)}");
        }