内存管理与并发处理
本文档介绍源生成器开发中的内存管理和并发处理技巧,包括避免内存泄漏、大型项目优化、线程安全等。
📋 文档信息
- 难度级别: 高级
- 预计阅读时间: 20 分钟
- 前置知识:
- C# 内存管理基础
- 多线程和并发编程基础
🎯 学习目标
完成本文档后,您将能够:
- ✅ 避免常见的内存泄漏
- ✅ 优化大型项目的内存使用
- ✅ 实现线程安全的生成器
- ✅ 正确处理取消令牌
- ✅ 使用线程安全的集合
内存管理
避免内存泄漏
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 内存安全的生成器
/// 展示如何避免常见的内存泄漏
/// </summary>
[Generator]
public class MemorySafeGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classData = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, ct) =>
{
// ❌ 错误:不要保存 SemanticModel 或 SyntaxNode
// return new { Model = ctx.SemanticModel, Node = ctx.Node };
// ✅ 正确:只提取需要的数据
var classSymbol = ctx.SemanticModel.GetDeclaredSymbol(
(ClassDeclarationSyntax)ctx.Node, ct) as INamedTypeSymbol;
if (classSymbol == null)
{
return null;
}
// ✅ 提取数据,不保存符号引用
return new ClassInfo(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
classSymbol.GetMembers().Length);
})
.Where(info => info != null);
context.RegisterSourceOutput(classData, (spc, info) =>
{
if (info == null) return;
// 生成代码
var code = GenerateCode(info);
spc.AddSource($"{info.Name}.g.cs", code);
});
}
private string GenerateCode(ClassInfo info)
{
return $@"
// 生成的代码
namespace {info.Namespace}
{{
partial class {info.Name}
{{
// 成员数量: {info.MemberCount}
}}
}}";
}
}
/// <summary>
/// 类信息 - 只包含基本数据类型
/// </summary>
public record ClassInfo(string Name, string Namespace, int MemberCount);大型项目优化
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 大型项目优化策略
/// 处理包含数千个文件的项目
/// </summary>
[Generator]
public class LargeProjectGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// ✅ 策略 1: 使用精确的过滤条件
// 尽早过滤,减少需要处理的节点数量
var targetClasses = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) =>
{
// ✅ 快速的语法检查
if (node is not ClassDeclarationSyntax classDecl)
{
return false;
}
// ✅ 检查是否有特定的特性
return classDecl.AttributeLists.Count > 0;
},
transform: (ctx, ct) =>
{
// ✅ 只在通过过滤的节点上执行语义分析
var classSymbol = ctx.SemanticModel.GetDeclaredSymbol(
(ClassDeclarationSyntax)ctx.Node, ct) as INamedTypeSymbol;
if (classSymbol == null)
{
return null;
}
// ✅ 检查特定的特性
var hasAttribute = classSymbol.GetAttributes()
.Any(a => a.AttributeClass?.Name == "GenerateAttribute");
if (!hasAttribute)
{
return null;
}
return ExtractClassData(classSymbol);
});
// ✅ 策略 2: 批量处理
// 收集所有数据后一次性生成
var allClasses = targetClasses
.Where(data => data != null)
.Collect();
context.RegisterSourceOutput(allClasses, (spc, classes) =>
{
// ✅ 批量生成可以共享一些计算
GenerateBatchCode(spc, classes);
});
}
private ClassData? ExtractClassData(INamedTypeSymbol classSymbol)
{
// 提取数据
return new ClassData(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
new List<PropertyData>());
}
private void GenerateBatchCode(
SourceProductionContext context,
ImmutableArray<ClassData?> classes)
{
// 批量生成代码
foreach (var classData in classes)
{
if (classData == null) continue;
var code = GenerateCodeForClass(classData);
context.AddSource($"{classData.ClassName}.g.cs", code);
}
}
private string GenerateCodeForClass(ClassData classData)
{
return $"// 生成的代码 for {classData.ClassName}";
}
}
/// <summary>
/// 类数据
/// </summary>
public record ClassData(
string ClassName,
string Namespace,
List<PropertyData> Properties);
/// <summary>
/// 属性数据
/// </summary>
public record PropertyData(string Name, string Type);并发处理
线程安全的生成器
csharp
using System.Collections.Concurrent;
using Microsoft.CodeAnalysis;
/// <summary>
/// 线程安全的生成器
/// 展示如何处理并发访问
/// </summary>
[Generator]
public class ThreadSafeGenerator : IIncrementalGenerator
{
// ✅ 使用线程安全的集合
private readonly ConcurrentDictionary<string, ClassData> _cache = new();
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classData = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, ct) =>
{
// ✅ Transform 方法可能在多个线程上并发执行
// 确保所有操作都是线程安全的
var classSymbol = ctx.SemanticModel.GetDeclaredSymbol(
(ClassDeclarationSyntax)ctx.Node, ct) as INamedTypeSymbol;
if (classSymbol == null)
{
return null;
}
// ✅ 使用线程安全的缓存
return _cache.GetOrAdd(
classSymbol.ToDisplayString(),
_ => ExtractClassData(classSymbol));
});
context.RegisterSourceOutput(classData, (spc, data) =>
{
// ✅ RegisterSourceOutput 的回调也可能并发执行
// 但 SourceProductionContext 是线程安全的
if (data == null) return;
var code = GenerateCode(data);
spc.AddSource($"{data.ClassName}.g.cs", code);
});
}
private ClassData ExtractClassData(INamedTypeSymbol classSymbol)
{
// 提取数据(线程安全)
return new ClassData(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
new List<PropertyData>());
}
private string GenerateCode(ClassData data)
{
// 生成代码(线程安全)
return $"// 代码 for {data.ClassName}";
}
}取消令牌处理
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 正确处理取消令牌
/// 展示如何响应取消请求
/// </summary>
[Generator]
public class CancellationAwareGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classData = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, ct) =>
{
// ✅ 定期检查取消令牌
ct.ThrowIfCancellationRequested();
var classSymbol = ctx.SemanticModel.GetDeclaredSymbol(
(ClassDeclarationSyntax)ctx.Node, ct) as INamedTypeSymbol;
if (classSymbol == null)
{
return null;
}
// ✅ 在长时间运行的操作前检查
ct.ThrowIfCancellationRequested();
return ProcessClass(classSymbol, ct);
});
context.RegisterSourceOutput(classData, (spc, data) =>
{
if (data == null) return;
// ✅ 在生成代码前检查取消
spc.CancellationToken.ThrowIfCancellationRequested();
var code = GenerateCode(data, spc.CancellationToken);
spc.AddSource($"{data.ClassName}.g.cs", code);
});
}
private ClassData? ProcessClass(INamedTypeSymbol classSymbol, CancellationToken ct)
{
var properties = new List<PropertyData>();
foreach (var member in classSymbol.GetMembers())
{
// ✅ 在循环中定期检查
ct.ThrowIfCancellationRequested();
if (member is IPropertySymbol property)
{
properties.Add(new PropertyData(
property.Name,
property.Type.ToDisplayString()));
}
}
return new ClassData(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
properties);
}
private string GenerateCode(ClassData data, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var builder = new StringBuilder();
builder.AppendLine($"// 代码 for {data.ClassName}");
// ✅ 在生成大量代码时定期检查
foreach (var property in data.Properties)
{
ct.ThrowIfCancellationRequested();
builder.AppendLine($"// Property: {property.Name}");
}
return builder.ToString();
}
}最佳实践
✅ 推荐做法
使用不可变集合
csharp// ✅ 好 public record Data(ImmutableArray<string> Items); // ❌ 不好 public record BadData(List<string> Items);只提取需要的数据
csharp// ✅ 好:只提取基本数据 return new ClassInfo( classSymbol.Name, classSymbol.ContainingNamespace.ToDisplayString()); // ❌ 不好:保存符号引用 return new { Symbol = classSymbol };使用线程安全的集合
csharp// ✅ 好 private readonly ConcurrentDictionary<string, ClassData> _cache = new(); // ❌ 不好 private readonly Dictionary<string, ClassData> _cache = new();定期检查取消令牌
csharp// ✅ 好 foreach (var item in items) { ct.ThrowIfCancellationRequested(); ProcessItem(item); } // ❌ 不好:从不检查取消令牌 foreach (var item in items) { ProcessItem(item); }
❌ 反模式
- 保存 SemanticModel 或 SyntaxNode
- 使用可变集合
- 使用共享状态
- 忽略取消令牌
🔑 关键要点
- 避免内存泄漏 - 不要保存符号引用和语法节点
- 使用不可变类型 - 使用 ImmutableArray 和 record
- 线程安全 - Transform 方法必须是线程安全的
- 取消令牌 - 定期检查取消令牌
- 批量处理 - 收集所有数据后一次性处理
📚 相关资源
📝 下一步
📝 文档质量保证
本文档遵循以下质量标准:
- ✅ 完整的目录结构
- ✅ 所有代码示例包含详细中文注释
- ✅ 包含最佳实践和反模式对比
- ✅ 包含真实使用场景
- ✅ 包含跨文档引用
- ✅ 内容完整,未因任何限制而精简
最后更新: 2025-01-21