C#如何安全、高效地玩转任何种类的内存之Span的本质

C#如何安全、高效地玩转任何种类的内存之Span的本质攻略

什么是Span

Span<T> 是 .NET Core 中新增的一种类型,它是一种类似于指针的结构体,表示一块连续的内存区域,其内容不一定是类型T的连续区域,可以是其他原语类型(如byte、int、long等)的连续区间。Span<T> 可以让我们高效地访问和读写内存在不进行垃圾回收的情况下,同时提高代码的可读性和安全性。

为什么要使用Span

在.NET Core中,Span<T>可以取代一部分App端耗费运行性能的,无法直接内联为CPU指令的Array类型(如byte[]、char[]等)。在某些场景下,使用Span比使用数组更快、更节省内存、更安全且易于维护。大多数情况下,Span可以完全替代传统Array类型。Span能够提供更快的数据访问速度,而且由于它并不在堆上分配内存,不会引发GC和堆内碎片。

Span的核心API

Span支持各种常见的API,比如基于范围(“range-based”)的访问、数组初始化、复制、遍历等等。以下是Span类型的几个重要API方法:

Slice

Slice方法可以创建一个新的Span,其所占据的连续内存段为当前原始Span所指定的一部分。从而可以通过该方法在Span中提供对子范围的非常高效的访问。

Span<byte> original = stackalloc byte[10];
original[0] = 1;
original[9] = 10;
Span<byte> slice = original.Slice(2, 6);
Console.WriteLine(slice[0]); // output is 3

Pin

针对临时的内存分配,Span还提供了一种手段——Pin,用于将Span关联起来的内存固定在内存中,防止GC在处理对象的过程中重复移动这个对象所提供的内存。如果你不经意间将 Span 的内存地址传递给其他代码,GC 稍后可能会移动该内存,导致应用程序崩溃。因此,通过固定内存而使其不可移动,即可避免该问题。

unsafe
{
    fixed (int* p = &source[0])
    fixed (int* q = &dest[0])
    {
        var src = new Span<int>(p, source.Length);
        var dst = new Span<int>(q, dest.Length);
        Span<int> slice = src.Slice(1, 2);
        MemoryExtensions.CopyTo(slice, dst);
    }
}

TryRead, TryWrite

TryRead和TryWrite方法能够更加高效地处理Span的访问。TryRead可以通过返回success值来判断是否成功读取,而不是抛出异常。TryWrite可以通过返回success值来判断是否成功写入,同时返回写入字节数。

Span<byte> buffer = new byte[4] { 1, 2, 3, 4 };
Span<int> casted = MemoryMarshal.Cast<byte, int>(buffer);

int val;
if(casted.TryRead(out val))
{
    Console.WriteLine(val);
}

Span的示例

数组快速排序

下面展示了使用Span实现的静态快速排序算法,其中排序的数组是int类型的。

public static void QuickSort(Span<int> span)
{
    if (span.Length <= 1)
    {
        return;
    }

    int pivotIndex = span.Length / 2;
    int pivotValue = span[pivotIndex];

    int left = 0;
    int right = span.Length - 1;

    while (left <= right)
    {
        while (span[left] < pivotValue)
        {
            left++;
        }

        while (span[right] > pivotValue)
        {
            right--;
        }

        if (left <= right)
        {
            int temp = span[left];
            span[left] = span[right];
            span[right] = temp;
            left++;
            right--;
        }
    }

    QuickSort(span.Slice(0, left));
    QuickSort(span.Slice(left));
}

比较两个Span

下面用举例的方式来展示比较两个Span的方法。

public static bool SequenceEqual(Span<byte> span1, Span<byte> span2)
{
    if (span1.Length != span2.Length)
    {
        return false;
    }

    for (int i = 0; i < span1.Length; i++)
    {
        if (span1[i] != span2[i])
        {
            return false;
        }
    }

    return true;
}

总结

Span是一个非常有用的数据类型,使用它有助于提高应用程序的性能和可读性。使用Span可以避免访问托管内存时的内存拷贝,提高程序的效率。Span API有助于简化代码,从而提高类型安全性。通过实践,Span也可以成为您的.NET Core开发工具箱中的一部分。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#如何安全、高效地玩转任何种类的内存之Span的本质 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • Entity Framework使用LINQ操作实体

    让我来详细讲解一下“Entity Framework使用LINQ操作实体”这个主题的完整攻略。 什么是Entity Framework? Entity Framework (EF) 是一个面向对象的关系数据库数据访问框架, 功能十分强大,能够提供对多种数据库的支持,包括 SQL Server、Oracle、MySQL 和 SQLite 等等。使用 Entit…

    C# 2023年6月1日
    00
  • 记一次 .NET 某设备监控系统 死锁分析

    一:背景 1. 讲故事 上周看了一位训练营朋友的dump,据朋友说他的程序卡死了,看完之后发现是一例经典的死锁问题,蛮有意思,这个案例算是学习 .NET高级调试 入门级的案例,这里和大家分享一下。 二:WinDbg 分析 1. 程序为什么会卡死 因为是窗体程序,所以看主线程的线程栈就好了,如果卡在 用户态 那这个问题相对容易解决,如果卡在 内核态 这个问题就…

    C# 2023年4月18日
    00
  • C#读写Config配置文件案例

    下面我会详细讲解“C#读写Config配置文件案例”的完整攻略。 什么是Config配置文件 Config配置文件是一种XML格式的配置文件,用于在应用程序中保存一些常见的配置数据。在C#中读写Config文件是一种常见的应用场景。 一个Config配置文件通常包含以下三种节点: configuration:root节点,表示当前文件是一个配置文件; con…

    C# 2023年6月1日
    00
  • C# 创建Excel气泡图的实例代码

    下面是详细的讲解。 1.前言 在使用软件的过程中,Excel无疑是一个非常常用的工具。其中,Excel中的图表功能可以让我们在数据分析的时候更加直观。 很多时候,我们需要通过程序生成Excel中的图表。本篇文章,将介绍如何使用C#代码创建Excel中的气泡图。 2.实现思路 气泡图是一种比较常用的图表形式,通过圆形的大小和位置表现数据,非常能够清晰地展示各个…

    C# 2023年6月3日
    00
  • websocket与C# socket相互通信

    web端代码就是js代码,C#有两种方式:使用第三方库,如Fleck,使用C#原生socket编程实现   web端: <!doctype html> <html lang=”zh-CN”> <head> <meta charset=”UTF-8″> <title>下发网站上文件到学生机</t…

    C# 2023年4月24日
    00
  • C# Path.GetFileNameWithoutExtension(string path):获取指定路径的文件名(不包括扩展名)

    知识点讲解 Path.GetFileNameWithoutExtension(string path) 方法是 C# 中 Path 类的静态方法之一,这个方法的作用是获取指定路径下文件的文件名,但不包括扩展名。该方法的返回值类型是 string。 方法参数 Path.GetFileNameWithoutExtension() 方法只接受一个 string 类…

    C# 2023年4月19日
    00
  • 将Access数据库中数据导入到SQL Server中的详细方法实例

    下面是将Access数据库中数据导入到SQL Server中的详细方法实例。 1. 概述 Access是Microsoft Office套件中的一个关系型数据库程序,而SQL Server是Microsoft开发的一种关系型数据库管理系统,两者都可以用来管理数据。有时,我们需要将Access数据库中的数据导入到SQL Server中,这样可以更好地管理和处理…

    C# 2023年5月31日
    00
  • ASP.NET Core使用固定窗口限流

    关于“ASP.NET Core使用固定窗口限流”的完整攻略,我会给你一个详细的解释: 什么是固定窗口限流 固定窗口限流是一种常用的限流算法,它将时间分成固定的窗口,每个窗口内的请求次数不能超过一定的阈值。举个例子:如果我们将时间分成1秒钟的窗口,设置每个窗口内最多只能处理10个请求,那么当某个窗口内的请求数超过10个时,则该窗口内的请求需要被限制。 如何使用…

    C# 2023年6月7日
    00
合作推广
合作推广
分享本页
返回顶部