你所不知道的.NET性能提升特性Memory<T>

 前面文章"你所不知道的.NET性能提升特性Span<T>"讲述过Span对性能的提升,但是Span有个问题,不能用在异步方法,如下图,使用会报异常,如果改成非异步方法错误消除,那么有类似Span的属性能在异步方法中运行吗?还真有,这就是Memory<T>。.

你所不知道的.NET性能提升特性Memory<T>

    我们先来看看官网的解释:类似 Span<T>, Memory<T> 表示内存的连续区域。但是Memory<T>,与引用结构不同Span<T>。这意味着 Memory<T> 可以放置在托管堆上,而 Span<T> 不能。因此,结构 Memory<T> 没有与 Span<T> 实例相同的限制。它可以用作类中的字段。它可以跨await``yield边界使用。

    从中可以得出:

     Memory<T> 表示内存的连续区域。这个跟Span<T>一样。
     Memory<T>可以放在托管堆上。意味可以装箱。
     Memory<T>可以用作类中的字段。实用。
     Memory<T>可以跨await``yield边界使用,使用在异步环境。
我们还是拿前面提到的那篇文章例子来做测试,如下:
static async Task  Main(string[] args){    var array = new byte[1000000];    var arrayMemory = new Memory<byte>(array);//装载到span    byte data = 0;    for (int i = 0; i < arrayMemory.Length; i++)    {        arrayMemory.Span[i] = data++;    }    int arraySum = 0;    for (int i= 0; i < arrayMemory.Length; i++)    {        arraySum += arrayMemory.Span[i];//这里必须转为Span    }    Console.WriteLine("Memory结果是: {0}", arraySum);    //Memory结果是: 127493856    var strs = new string[] { "java", "c++", "c#", "go", "python" };    var slice = new Memory<string>(strs, 2, 2);    await RunMemory(slice,strs);}private static async Task RunMemory(Memory<string> memory, string[] strs){    for (int ctr = 0; ctr < memory.Length; ctr++)        memory.Span[ctr] += "牛";    foreach (var value in strs)        Console.Write($"{value}  ");    // 结果 java  c++  c#牛  go牛  python}

在异步方法中可以使用,注意赋值或者读取需要转换Span类型才行,另外Memory不支持遍历(具体大家可以查一下资料),上面例子用了for循环。

结语

    这里列举了Memory<T>在异步方法的应用和元素切片的使用,还有作为字段和接口等大家可以自己试一试,和Span<T>一样,通常Memory<T>都是包裹数组、字符串,用法也基本相同,只是应用场景不一样而已。参数,异步方法应该使用Memory,同步方法用Span;同一Memory<T>实例不能同时被多个消费者使用等。