将一些代码升级为异步,并意识到Span<T>不能用于async方法.

调用Span<T>.ToArray()以每次分配为代价来修复该问题.

在这种情况下,你能给出一个如何避免分配的模式吗?

using ISO9660.Logical;

namespace ISO9660.Physical;

public static class DiscExtensions
{
    public static async Task ReadFileRawAsync(this Disc disc, IsoFileSystemEntryFile file, Stream stream)
    {
        await ReadFileAsync(disc, file, stream, ReadFileRaw);
    }

    public static async Task ReadFileUserAsync(this Disc disc, IsoFileSystemEntryFile file, Stream stream)
    {
        await ReadFileAsync(disc, file, stream, ReadFileUser);
    }

    private static async Task ReadFileAsync(Disc disc, IsoFileSystemEntryFile file, Stream stream, ReadFileHandler handler)
    {
        int position = (int)file.Position;

        Track track = disc.Tracks.FirstOrDefault(s => position >= s.Position)
                      ?? throw new InvalidOperationException("Failed to determine track for file.");

        int sectors = (int)Math.Ceiling((double)file.Length / track.Sector.GetUserDataLength());

        for (int i = position; i < position + sectors; i++)
        {
            ISector sector = await track.ReadSectorAsync(i);

            byte[] array = handler(file, stream, sector).ToArray(); // sucks

            await stream.WriteAsync(array);
        }
    }

    private static Span<byte> ReadFileRaw(IsoFileSystemEntryFile file, Stream stream, ISector sector)
    {
        Span<byte> data = sector.GetData();

        int size = data.Length;

        Span<byte> span = data[..size];

        return span;
    }

    private static Span<byte> ReadFileUser(IsoFileSystemEntryFile file, Stream stream, ISector sector)
    {
        Span<byte> data = sector.GetUserData();

        int size = (int)Math.Min(Math.Max(file.Length - stream.Length, 0), data.Length);

        Span<byte> span = data[..size];

        return span;
    }

    private delegate Span<byte> ReadFileHandler(IsoFileSystemEntryFile file, Stream stream, ISector sector);
}

ISector接口:

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

namespace ISO9660.Physical;

public interface ISector
{
    int Length { get; }

    Span<byte> GetData();

    Span<byte> GetUserData();

    int GetUserDataLength();

    public static Span<byte> GetSpan<T>(scoped ref T sector, int start, int length)
        where T : struct, ISector
    {
        var span = MemoryMarshal.CreateSpan(ref sector, 1);

        var bytes = MemoryMarshal.AsBytes(span);

        var slice = bytes.Slice(start, length);

        return slice;
    }
    
    public static ISector Read<T>(Stream stream)
        where T : struct, ISector
    {
        var size = Unsafe.SizeOf<T>();

        Span<byte> span = stackalloc byte[size];

        stream.ReadExactly(span);

        var read = MemoryMarshal.Read<T>(span);

        return read;
    }

    public static async Task<ISector> ReadAsync<T>(Stream stream)
        where T : struct, ISector
    {
        var buffer = new byte[Unsafe.SizeOf<T>()];

        await stream.ReadExactlyAsync(buffer);

        var sector = MemoryMarshal.Read<T>(buffer);

        return sector;
    }
}

ISector实现示例:

using JetBrains.Annotations;

namespace ISO9660.Physical;

public unsafe struct SectorRawMode1 : ISector
{
    private const int UserDataLength = 2048;

    private const int UserDataPosition = 16;

    [UsedImplicitly]
    public fixed byte Sync[12];

    [UsedImplicitly]
    public fixed byte Header[4];

    [UsedImplicitly]
    public fixed byte UserData[UserDataLength];

    [UsedImplicitly]
    public fixed byte Edc[4];

    [UsedImplicitly]
    public fixed byte Intermediate[8];

    [UsedImplicitly]
    public fixed byte PParity[172];

    [UsedImplicitly]
    public fixed byte QParity[104];

    public readonly int Length => 2352;

    public Span<byte> GetData()
    {
        return ISector.GetSpan(ref this, 0, Length);
    }

    public Span<byte> GetUserData()
    {
        return ISector.GetSpan(ref this, UserDataPosition, UserDataLength);
    }

    public readonly int GetUserDataLength()
    {
        return UserDataLength;
    }
}

推荐答案

Span<T>更改为Memory<T>,然后向上推送更改.Memory<T>类似于异步的Span<T>(实际上).这个 idea 不同于Span,Memory可以驻留在堆上(因此不能引用stackalloc).

没有MemoryMarshal.CreateMemory,所以你必须用老式的方法来做:别针,取地址,然后构造一个Memory形式的指针.示例如何做到这一点:https://stackoverflow.com/a/52191681/14768请注意,内存管理器分配比缓冲区分配小得多,您不需要复制.

编写代码时要非常小心;这个内存管理器的构造函数确实接受Span,也就是整个ISector,它将在async个调用中存活;但如果您不能处理它,就会发生内存泄漏.此外,如果ISector来自堆栈而不是堆,那就是你的责任,你会把它扔进垃圾堆.

Csharp相关问答推荐

Serilog SQL服务器接收器使用UTC作为时间戳

C#类主构造函数中的调试参数

有没有一种方法可以在包含混合文本的标签中嵌入超链接?

应该使用哪一个?"_counter += 1 OR互锁增量(ref_counter)"""

如何在Reflection. Emit中使用具有运行时定义的类型参数的泛型类型

读取配置文件(mytest. exe. config)

在C#WinUI中,一个关于System的崩溃."由于未知原因导致执行不例外"

如何将Kafka消息时间戳转换为C#中的日期和时间格式?

HttpConext.Request.Path和HttpConext.GetEndpoint()之间的差异

ASP.NET Core AutoMapper:如何解决错误 CS0121调用在以下方法或属性之间不明确

将字节转换为 struct 并返回

在不添加不必要的尾随零的情况下本地化浮点型?

如何使用MailKit删除邮箱?

具有以接口为其类型的属性的接口;类指定接口的实现,但无效

Blazor:搜索框在第一次搜索时不搜索

用于获取字符串的最后12个字符的正则表达式(空格除外)

如何消除Visual Studio错误,因为它不识别集合表达式的新C#12语法?

ASP.NET Core 8 Web API:如何添加版本控制?

C#-如何将int引用获取到byte[]

外部应用&&的LINQ;左外部连接&类似于PostgreSQL的查询