C# 基于ReadOnlySequence和ReadOnlySequenceSegment的简单封装

最近在研究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)}");
        }

 

posted @ 2025-06-10 11:04  WmW  阅读(20)  评论(0)    收藏  举报