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

浙公网安备 33010602011771号