Skip to content

内存管理与并发处理

本文档介绍源生成器开发中的内存管理和并发处理技巧,包括避免内存泄漏、大型项目优化、线程安全等。

📋 文档信息

  • 难度级别: 高级
  • 预计阅读时间: 20 分钟
  • 前置知识:
    • C# 内存管理基础
    • 多线程和并发编程基础

🎯 学习目标

完成本文档后,您将能够:

  1. ✅ 避免常见的内存泄漏
  2. ✅ 优化大型项目的内存使用
  3. ✅ 实现线程安全的生成器
  4. ✅ 正确处理取消令牌
  5. ✅ 使用线程安全的集合

内存管理

避免内存泄漏

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();
    }
}

最佳实践

✅ 推荐做法

  1. 使用不可变集合

    csharp
    // ✅ 好
    public record Data(ImmutableArray<string> Items);
    
    // ❌ 不好
    public record BadData(List<string> Items);
  2. 只提取需要的数据

    csharp
    // ✅ 好:只提取基本数据
    return new ClassInfo(
        classSymbol.Name,
        classSymbol.ContainingNamespace.ToDisplayString());
    
    // ❌ 不好:保存符号引用
    return new { Symbol = classSymbol };
  3. 使用线程安全的集合

    csharp
    // ✅ 好
    private readonly ConcurrentDictionary<string, ClassData> _cache = new();
    
    // ❌ 不好
    private readonly Dictionary<string, ClassData> _cache = new();
  4. 定期检查取消令牌

    csharp
    // ✅ 好
    foreach (var item in items)
    {
        ct.ThrowIfCancellationRequested();
        ProcessItem(item);
    }
    
    // ❌ 不好:从不检查取消令牌
    foreach (var item in items)
    {
        ProcessItem(item);
    }

❌ 反模式

  1. 保存 SemanticModel 或 SyntaxNode
  2. 使用可变集合
  3. 使用共享状态
  4. 忽略取消令牌

🔑 关键要点

  1. 避免内存泄漏 - 不要保存符号引用和语法节点
  2. 使用不可变类型 - 使用 ImmutableArray 和 record
  3. 线程安全 - Transform 方法必须是线程安全的
  4. 取消令牌 - 定期检查取消令牌
  5. 批量处理 - 收集所有数据后一次性处理

📚 相关资源

📝 下一步

  1. 探索 高级实战场景 应用高级模式
  2. 了解 监控和诊断 监控生成器性能
  3. 学习 性能优化 优化生成器性能

📝 文档质量保证

本文档遵循以下质量标准:

  • ✅ 完整的目录结构
  • ✅ 所有代码示例包含详细中文注释
  • ✅ 包含最佳实践和反模式对比
  • ✅ 包含真实使用场景
  • ✅ 包含跨文档引用
  • ✅ 内容完整,未因任何限制而精简

最后更新: 2025-01-21

基于 MIT 许可发布