admin管理员组文章数量:1442757
C# 12 中的 Span<T> 和 Memory<T>:高级开发人员的性能助推器
作为ASP.NET开发人员,我们一直在寻找能让我们的Web应用程序运行得更快、更高效的方法。Span<T>
和Memory<T>
这两个强大的工具就能帮我们达成这一目标。它们于几年前被引入,如今已成为编写高性能C#代码必不可少的部分。
让我们通过实际示例以及针对2024年C# 12的一些技巧,来探讨如何在ASP.NET应用程序中有效地使用Span<T>
和Memory<T>
。
Span<T>
和Memory<T>
是什么?
让我们先从基础知识讲起:
Span<T>
是一种表示连续内存块的类型。它可用于处理数组、字符串或非托管内存,而且无需创建副本。
Memory<T>
与Span<T>
类似,但它可用于异步方法中,并且能存储在字段里。
可以把Span<T>
想象成能直接操作的内存视图,而Memory<T>
则是对该内存的引用,能更自由地传递。
下面让我们来看一些在常见的ASP.NET场景中,Span<T>
和Memory<T>
能发挥重大作用的情况:
Span<T>
1. 解析请求数据
设想你正在构建一个会接收大量JSON数据的API。你可以使用Span<T>
更高效地解析数据,而不必为每条数据都分配新的字符串:
[HttpPost]
publicIActionResultProcessOrder([FromBody]string orderJson)
{
ReadOnlySpan<char> jsonSpan = orderJson.AsSpan();
// 在不分配新字符串的情况下查找 "totalAmount" 字段
int startIndex = jsonSpan.IndexOf("\"totalAmount\":")+"\"totalAmount\":".Length;
int endIndex = jsonSpan.Slice(startIndex).IndexOf(',');
if(decimal.TryParse(jsonSpan.Slice(startIndex, endIndex),outdecimal totalAmount))
{
// 处理订单...
returnOk($"Order processed with total amount: {totalAmount}");
}
returnBadRequest("Invalid order data");
}
这种方法减少了内存分配,提升了性能,尤其在处理大型有效载荷时效果显著。
2. 高效的响应编写
在发送响应(特别是大型响应)时,Span<T>
有助于优化内存使用:
[HttpGet]
publicIActionResultGetLargeData()
{
constint bufferSize =*;// 1 MB缓冲区
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
int dataSize =GenerateLargeData(buffer.AsSpan());
returnFile(buffer.AsMemory(, dataSize).ToArray(),"application/octet-stream","large-data.bin");
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
privateintGenerateLargeData(Span<byte> buffer)
{
// 用数据填充缓冲区...
return/* 实际数据大小 */;
}
这个示例结合使用ArrayPool<T>
与Span<T>
和Memory<T>
,能在不过度分配内存的情况下高效地处理大型数据。
3. 中间件中的URL解析
自定义中间件通常需要检查URL。以下展示了如何高效地进行这项操作:
代码语言:javascript代码运行次数:0运行复制public classCustomUrlMiddleware
{
privatereadonlyRequestDelegate _next;
publicCustomUrlMiddleware(RequestDelegate next)=> _next = next;
publicTaskInvokeAsync(HttpContext context)
{
ReadOnlySpan<char> path = context.Request.Path.Value.AsSpan();
if(path.StartsWith("/api/".AsSpan()))
{
// 处理API请求
context.Items["IsApiRequest"]=true;
}
return_next(context);
}
}
这个中间件在检查URL时无需分配新的字符串,这对于高流量应用程序来说非常有用。
Memory<T>
在需要以下操作时可使用Memory<T>
:
- 跨异步边界处理内存。
- 将对内存段的引用作为类中的字段进行存储。
- 将内存引用传递给期望接收
Memory<T>
的方法。
1. 异步文件上传处理
代码语言:javascript代码运行次数:0运行复制[HttpPost("upload")]
publicasyncTask<IActionResult>UploadFile(IFormFile file)
{
if(file.Length >10_000_000)// 10 MB限制
returnBadRequest("File too large");
var memory =newMemory<byte>(newbyte[file.Length]);
using(var stream = file.OpenReadStream())
{
await stream.ReadAsync(memory);
}
awaitProcessUploadedFileAsync(memory);
returnOk("File processed successfully");
}
privateasyncTaskProcessUploadedFileAsync(Memory<byte> fileData)
{
// 模拟一些异步处理
await Task.Delay();// 实际处理的占位符
// 示例:统计非零字节数
int nonZeroBytes =;
foreach(byte b in fileData.Span)
{
if(b!=) nonZeroBytes++;
}
Console.WriteLine($"Processed file with {nonZeroBytes} non-zero bytes");
}
这个示例展示了如何使用Memory<T>
在异步方法间处理文件数据,而无需不必要的复制操作。
2. 缓存大型对象
在ASP.NET应用程序中缓存大型对象时,Memory<T>
会很有用:
public classLargeObjectCache
{
privateMemory<byte> cachedData;
publicasyncTask<Memory<byte>>GetOrCreateAsync(Func<Task<byte[]>> createFunc)
{
if(cachedData.IsEmpty)
{
byte[] newData =awaitcreateFunc();
cachedData =newMemory<byte>(newData);
}
return cachedData;
}
}
// 在控制器中的用法
[HttpGet("large-data")]
publicasyncTask<IActionResult>GetLargeData([FromServices]LargeObjectCache cache)
{
var data =await cache.GetOrCreateAsync(async()=>
{
// 模拟获取大型数据
await Task.Delay();
returnnewbyte[1_000_000];// 1 MB的数据
});
returnFile(data.ToArray(),"application/octet-stream");
}
3. 在后台任务中高效构建字符串
对于构建大型字符串的后台任务,Memory<T>
可能比StringBuilder
更高效:
public classReportGenerator:BackgroundService
{
protectedoverrideasyncTaskExecuteAsync(CancellationToken stoppingToken)
{
while(!stoppingToken.IsCancellationRequested)
{
awaitGenerateReportAsync();
await Task.Delay(TimeSpan.FromHours(), stoppingToken);
}
}
privateasyncTaskGenerateReportAsync()
{
var writer =newArrayBufferWriter<char>(initialCapacity:*);// 初始容量为1 MB
awaitWriteReportHeaderAsync(writer);
awaitWriteReportBodyAsync(writer);
awaitWriteReportFooterAsync(writer);
string report =newstring(writer.WrittenMemory.Span);
awaitSaveReportAsync(report);
}
privateasyncTaskWriteReportHeaderAsync(IBufferWriter<char> writer)
{
var memory = writer.GetMemory();
int written = System.Text.Encoding.UTF8.GetBytes("Report Header\n", memory.Span);
writer.Advance(written);
await Task.Delay();// 模拟一些异步工作
}
// 用于WriteReportBodyAsync和WriteReportFooterAsync的类似方法
privateasyncTaskSaveReportAsync(string report)
{
// 将报告保存到数据库或文件
await File.WriteAllTextAsync("report.txt", report);
}
}
这个示例展示了如何在后台任务中结合使用Memory<T>
和IBufferWriter<T>
来高效构建字符串,这在ASP.NET应用程序中对于诸如生成报告或数据处理之类的任务很常见。
对于希望优化应用程序的ASP.NET开发人员来说,Span<T>
和Memory<T>
是强大的工具。通过使用这些类型,你可以编写更高效的代码,这些代码占用更少的内存且运行速度更快。
本文标签: C 12 中的 SpanampltTampgt 和 MemoryampltTampgt高级开发人员的性能助推器
版权声明:本文标题:C# 12 中的 Span&lt;T&gt; 和 Memory&lt;T&gt;:高级开发人员的性能助推器 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1748048368a2797835.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论