一、说明

内存池是RRQMCore的最重要的组成部分,在RRQMSocket产品中,BytePool贯穿始终。所以熟悉使用BytePool,也是非常重要的。

Nuget Package:RRQMCore

二、功能

内存池(BytePool)的最小实现单体是内存块(ByteBlock),它是继承自Stream的类,拥有和MemoryStream一样的功能和RRQM的增强功能。而且拥有释放回收能力,所以如果有MemoryStream的使用需求的话,就可以完全让ByteBlock替代。

三、创建

BytePool是静态类,无需创建,直接使用即可。ByteBlock可通过BytePool创建,也可以直接new对象,这两者实际操作一致。
注意:创建的ByteBlock必须释放(Dispose),虽然不会内存泄露,但是会影响性能。

  1. ByteBlock byteBlock1 = new ByteBlock(byteSize:1024*1024,equalSize:false);
  2. byteBlock1.Dispose();
  3. ByteBlock byteBlock2 = BytePool.GetByteBlock(byteSize:1024*1024,equalSize:false);
  4. byteBlock2.Dispose();
  5. using (ByteBlock byteBlock3=new ByteBlock())
  6. {
  7. }

四、使用

基础使用和MemoryStream一致,下面仅介绍特定使用。

4.1 写入、读取数据对象

  1. using (ByteBlock byteBlock = new ByteBlock())
  2. {
  3. byteBlock.Write(byte.MaxValue);
  4. byteBlock.Write(int.MaxValue);
  5. byteBlock.Write(long.MaxValue);
  6. byteBlock.Write("RRQM");
  7. byteBlock.WriteObject(new Person(), SerializationType.Json);
  8. byteBlock.WriteBytesPackage(new byte[1024]);
  9. byteBlock.Pos = 0;//读取时,先将游标移动到初始写入的位置,然后按写入顺序,依次读取
  10. byte ByteValue = byteBlock.ReadByte();
  11. int IntValue = byteBlock.ReadInt32();
  12. long LongValue = byteBlock.ReadInt64();
  13. string StringValue = byteBlock.ReadString();
  14. Person ObjectValue = byteBlock.ReadObject<Person>(SerializationType.Json);
  15. byte[] BytesValue = byteBlock.ReadBytesPackage();
  16. }

4.2 多线程同步协作(Hold)

在多线程异步时,设计架构应当遵守谁(Thread)创建的ByteBlock,由谁释放,这样就能很好的避免未释放的情况发生。实际上RRQMSocket中,就是秉承这样的设计,任何非用户创建的ByteBlock,都会由创建的线程最后释放。但是在使用中,经常出现异步多线程的操作。
以RRQMSocket的TcpClient为例。如果直接在收到数据时,使用Task异步,则必定会发生关于ByteBlock的各种各样的异常。
原因非常简单,byteBlock对象在到达HandleReceivedData时,触发Task异步,此时触发线程会立即返回,并释放byteBlock,而Task异步线程会滞后,然后试图从已释放的byteBlock中获取数据,所以,必定发生异常。

  1. public class MyTClient : TcpClient
  2. {
  3. protected override void HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
  4. {
  5. Task.Run(()=>
  6. {
  7. string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
  8. Console.WriteLine($"已接收到信息:{mes}");
  9. });
  10. }
  11. }

解决方法也非常简单,只需要在异步前锁定,然后使用完成后取消锁定,且不用再调用Dispose。

  1. public class MyTClient : TcpClient
  2. {
  3. protected override void HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
  4. {
  5. byteBlock.SetHolding(true);//异步前锁定
  6. Task.Run(()=>
  7. {
  8. string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
  9. byteBlock.SetHolding(false);//使用完成后取消锁定,且不用再调用Dispose
  10. Console.WriteLine($"已接收到信息:{mes}");
  11. });
  12. }
  13. }