提问人:aybe 提问时间:11/7/2023 最后编辑:aybe 更新时间:11/7/2023 访问量:74
避免 Span<T>。ToArray() 在异步方法中?
Avoiding Span<T>.ToArray() in an async method?
问:
将一些代码升级为异步代码,并意识到不能在方法中使用。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;
}
}
答:
2赞
Joshua
11/7/2023
#1
更改为并向上推送更改。 类似于异步(在实践中)。这个想法不像 a , a 可以存在于堆上(因此不能指代 )。Span<T>
Memory<T>
Memory<T>
Span<T>
Span
Memory
stackalloc
没有,所以你必须用老式的方式去做:固定,获取地址,并构造一个指针。如何执行此操作的示例: https://stackoverflow.com/a/52191681/14768 请注意,MemoryManager 分配远小于缓冲区分配,因此无需复制。MemoryMarshal.CreateMemory
Memory
在编写代码时要非常小心;这个 MemoryManager 的构造函数确实接受一个 是整个 ,并且它将在调用中存在;但是,如果您未能处理它,则会泄漏内存。此外,如果它们来自堆栈而不是堆,那么它就在你身上,你会丢弃堆栈。Span
ISector
async
ISector
评论
var
CancellationToken
var position = (int)file.Position;
<-- ISO 9660 允许最大长度为 8TB 的卷,但您的代码假定有 32 位 (4GB) 的限制。你应该在这里使用。Int64
ReadFileHandler