异步与并行 LINQ:提升.NET应用程序性能的利器

引言

在现代软件开发中,性能优化是一个永恒的主题。随着多核处理器的普及和大数据处理需求的增加,如何充分利用硬件资源,提高应用程序的响应速度和吞吐量,成为每个开发者必须面对的挑战。在.NET生态系统中,LINQ (Language Integrated Query) 作为一种强大的数据查询和处理工具,提供了异步和并行处理的能力,为开发高性能应用程序提供了有力支持。

本文将深入探讨LINQ中的异步和并行处理技术,包括异步LINQ(基于Task的异步模式)和并行LINQ(PLINQ)。我们将通过详细的代码示例,说明这些技术的工作原理、使用场景以及性能优化策略,帮助您在实际项目中合理应用这些技术,提升应用程序性能。

文章目录

    • 引言
    • 异步LINQ:优化I/O密集型操作
      • 什么是异步LINQ?
      • LINQ异步操作的基本模式
      • 异步LINQ扩展方法
      • 在LINQ查询中使用异步Lambda表达式
      • 异步流 (IAsyncEnumerable)
      • 异步LINQ的优势与限制
    • 并行LINQ (PLINQ):优化CPU密集型操作
      • 什么是并行LINQ?
      • PLINQ的基本用法
      • PLINQ的高级选项
        • 1. 控制并行度
        • 2. 保持元素顺序
        • 3. 合并和分区选项
        • 4. 处理并行查询的结果
      • 异常处理
      • 并行LINQ的性能考量
    • 异步LINQ vs 并行LINQ:如何选择?
      • 功能对比
      • 选择指南
    • 性能优化最佳实践
      • 异步LINQ性能优化
      • 并行LINQ性能优化
    • 实际应用场景
      • 异步LINQ应用场景
      • 并行LINQ应用场景
    • 结论
    • 学习资源

异步LINQ:优化I/O密集型操作

什么是异步LINQ?

异步LINQ是指在LINQ查询中应用asyncawait关键字,使查询操作能够异步执行,从而避免阻塞主线程。这种模式特别适用于I/O密集型操作,如网络请求、文件读写和数据库访问等。

在.NET中,异步LINQ主要通过以下几种方式实现:

  1. 使用LINQ扩展方法的异步版本(如ToListAsyncFirstOrDefaultAsync等)
  2. 在LINQ查询中使用异步Lambda表达式
  3. 结合Task Parallel Library (TPL)使用LINQ

LINQ异步操作的基本模式

应用程序 LINQ查询 数据源 创建异步LINQ查询 发送异步请求 继续执行其他操作 返回结果 通过await获取结果 应用程序 LINQ查询 数据源

异步LINQ扩展方法

Entity Framework Core提供了许多异步版本的LINQ扩展方法,这些方法名称通常以"Async"后缀结尾:

// 使用Entity Framework Core的异步LINQ方法
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class CustomerService
{
    private readonly ApplicationDbContext _context;
    
    public CustomerService(ApplicationDbContext context)
    {
        _context = context;
    }
    
    // 异步获取客户列表
    public async Task<List<Customer>> GetCustomersAsync()
    {
        // 使用ToListAsync异步执行查询并返回结果
        return await _context.Customers
            .Where(c => c.IsActive)
            .OrderBy(c => c.LastName)
            .ToListAsync();
    }
    
    // 异步获取单个客户
    public async Task<Customer> GetCustomerByIdAsync(int id)
    {
        // 使用FirstOrDefaultAsync异步查找单个实体
        return await _context.Customers
            .FirstOrDefaultAsync(c => c.Id == id);
    }
    
    // 异步检查是否存在满足条件的客户
    public async Task<bool> AnyActiveCustomersAsync()
    {
        // 使用AnyAsync异步检查是否存在满足条件的实体
        return await _context.Customers
            .AnyAsync(c => c.IsActive && c.Orders.Count > 0);
    }
    
    // 异步获取客户数量
    public async Task<int> GetCustomerCountAsync()
    {
        // 使用CountAsync异步计算满足条件的实体数量
        return await _context.Customers
            .Where(c => c.IsActive)
            .CountAsync();
    }
}

在LINQ查询中使用异步Lambda表达式

有时,我们需要在LINQ查询的Lambda表达式中执行异步操作。在这种情况下,我们可以结合SelectTask.WhenAll来实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

public class ExternalApiService
{
    private readonly HttpClient _httpClient;
    
    public ExternalApiService()
    {
        _httpClient = new HttpClient();
    }
    
    // 使用异步Lambda处理集合
    public async Task<List<UserData>> EnrichUsersWithApiDataAsync(List<User> users)
    {
        // 创建任务列表
        var tasks = users.Select(async user => 
        {
            // 对每个用户异步调用API获取附加数据
            var apiData = await FetchUserDataFromApiAsync(user.Id);
            
            // 返回组合后的数据
            return new UserData
            {
                User = user,
                ExternalData = apiData,
                ProcessedDate = DateTime.Now
            };
        });
        
        // 等待所有任务完成并获取结果
        var results = await Task.WhenAll(tasks);
        
        // 转换为列表并返回
        return results.ToList();
    }
    
    // 模拟从API获取用户数据
    private async Task<ExternalData> FetchUserDataFromApiAsync(int userId)
    {
        // 构建API请求URL
        string url = $"https://api.example.com/users/{userId}";
        
        // 发送异步请求
        var response = await _httpClient.GetAsync(url);
        
        // 确保请求成功
        response.EnsureSuccessStatusCode();
        
        // 异步读取响应内容
        var content = await response.Content.ReadAsStringAsync();
        
        // 这里应该有JSON反序列化代码,为简化示例,直接返回模拟数据
        return new ExternalData 
        { 
            Score = new Random().Next(1, 100),
            LastActivity = DateTime.Now.AddDays(-new Random().Next(1, 30))
        };
    }
}

// 模型类
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public class ExternalData
{
    public int Score { get; set; }
    public DateTime LastActivity { get; set; }
}

public class UserData
{
    public User User { get; set; }
    public ExternalData ExternalData { get; set; }
    public DateTime ProcessedDate { get; set; }
}

异步流 (IAsyncEnumerable)

在.NET Core 3.0及更高版本中,引入了IAsyncEnumerable接口和await foreach语法,使得异步数据流处理更加直观:

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

public class FileProcessor
{
    // 返回异步枚举,允许异步迭代
    public async IAsyncEnumerable<string> ReadLargeFileAsync(string filePath)
    {
        // 异步打开文件
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        using var reader = new StreamReader(fileStream);
        
        // 逐行异步读取并返回
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            // 在返回每一行之前可以进行处理
            if (!string.IsNullOrWhiteSpace(line))
            {
                yield return line;
            }
        }
    }
    
    // 使用异步流处理大文件
    public async Task ProcessLargeFileAsync(string filePath)
    {
        int lineCount = 0;
        int charCount = 0;
        
        // 使用await foreach异步迭代
        await foreach (var line in ReadLargeFileAsync(filePath))
        {
            lineCount++;
            charCount += line.Length;
            
            // 处理每一行...
            Console.WriteLine($"处理第{lineCount}行,字符数:{line.Length}");
            
            // 可以在这里执行更多异步操作
            await Task.Delay(1); // 模拟一些异步处理
        }
        
        Console.WriteLine($"总行数:{lineCount},总字符数:{charCount}");
    }
}

异步LINQ的优势与限制

优势:

  1. 提升响应性:异步操作不会阻塞UI线程,使应用程序保持响应
  2. 提高吞吐量:在I/O等待期间可以执行其他工作,提高资源利用率
  3. 简化异步编程模型:与传统的回调方式相比,async/await模式更易于理解和维护

限制:

  1. 不是所有LINQ操作都有异步版本:标准LINQ to Objects没有内置异步方法
  2. 调试和错误处理可能更复杂:异步代码的堆栈跟踪可能不如同步代码直观
  3. 可能引入额外开销:对于简单快速的操作,异步可能增加不必要的复杂性和开销

并行LINQ (PLINQ):优化CPU密集型操作

什么是并行LINQ?

并行LINQ (PLINQ) 是LINQ的并行实现,设计用于在多核处理器上并行执行查询,从而提高CPU密集型操作的性能。PLINQ利用了Task Parallel Library (TPL),通过在多个线程上分配工作来加速计算密集型查询。

PLINQ通过简单地调用.AsParallel()方法就可以将普通LINQ查询转换为并行查询:

AsParallel
ForAll/ToList等
标准LINQ查询
并行LINQ查询
并行处理结果

PLINQ的基本用法

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

public class PlinkExamples
{
    public void BasicPlinqExample()
    {
        // 创建一个大型集合用于演示
        var numbers = Enumerable.Range(1, 10000000).ToList();
        
        // 创建一个计时器
        var stopwatch = new Stopwatch();
        
        // 使用标准LINQ
        stopwatch.Start();
        var evenSquaresLinq = numbers
            .Where(n => n % 2 == 0)
            .Select(n => 
            {
                // 模拟一些计算密集型工作
                return (long)Math.Pow(n, 2);
            })
            .ToList();
        stopwatch.Stop();
        
        Console.WriteLine($"标准LINQ耗时: {stopwatch.ElapsedMilliseconds}ms");
        
        // 重置计时器
        stopwatch.Reset();
        
        // 使用PLINQ
        stopwatch.Start();
        var evenSquaresPlinq = numbers
            .AsParallel()                // 将查询转换为并行查询
            .Where(n => n % 2 == 0)      // 并行执行过滤操作
            .Select(n => 
            {
                // 并行执行转换操作
                return (long)Math.Pow(n, 2);
            })
            .ToList();                   // 合并结果
        stopwatch.Stop();
        
        Console.WriteLine($"PLINQ耗时: {stopwatch.ElapsedMilliseconds}ms");
        
        // 验证结果相同
        bool resultsMatch = evenSquaresLinq.Count == evenSquaresPlinq.Count &&
                        evenSquaresLinq.SequenceEqual(evenSquaresPlinq);
        Console.WriteLine($"结果匹配: {resultsMatch}");
    }
}

PLINQ的高级选项

PLINQ提供了多种高级选项来控制并行处理的行为:

1. 控制并行度
// 限制PLINQ使用的最大线程数
var result = collection
    .AsParallel()
    .WithDegreeOfParallelism(Environment.ProcessorCount) // 使用逻辑处理器数量的线程
    .Where(item => SomeExpensiveTest(item))
    .ToList();
2. 保持元素顺序

默认情况下,PLINQ可能不保证结果的顺序与原始集合相同。使用AsOrdered()可以保持顺序:

// 保持与输入序列相同的顺序
var result = collection
    .AsParallel()
    .AsOrdered() // 保持元素顺序
    .Where(item => item.IsValid)
    .Select(item => ProcessItem(item))
    .ToList();
3. 合并和分区选项
using System;
using System.Collections.Generic;
using System.Linq;

public class PlinqPartitioningExample
{
    public void DemonstratePartitioning()
    {
        // 创建示例数据
        var customers = Enumerable.Range(1, 5000)
            .Select(i => new Customer
            {
                Id = i,
                Name = $"Customer {i}",
                Orders = Enumerable.Range(1, new Random(i).Next(1, 20))
                    .Select(o => new Order { Id = o, Amount = new Random(i * o).Next(10, 1000) })
                    .ToList()
            })
            .ToList();
        
        // 使用并行查询处理客户订单
        var result = customers
            .AsParallel()
            // 指定如何将数据分区到线程
            .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            // 使用自定义分区策略(基于客户订单数量)
            .OrderBy(c => c.Orders.Count)
            .Select(c => new 
            {
                CustomerId = c.Id,
                OrderCount = c.Orders.Count,
                TotalAmount = c.Orders.Sum(o => o.Amount),
                AverageAmount = c.Orders.Average(o => o.Amount)
            })
            .ToList();
        
        Console.WriteLine($"处理完成。共{result.Count}个客户的统计信息。");
    }
    
    // 客户模型类
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Order> Orders { get; set; } = new List<Order>();
    }
    
    // 订单模型类
    public class Order
    {
        public int Id { get; set; }
        public decimal Amount { get; set; }
    }
}
4. 处理并行查询的结果

PLINQ提供了ForAll方法,用于并行处理查询结果:

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;

public class PlinqResultProcessing
{
    public void ProcessResultsInParallel()
    {
        // 用于存储处理结果的线程安全集合
        var resultBag = new ConcurrentBag<string>();
        
        // 跟踪使用的线程
        var usedThreads = new ConcurrentDictionary<int, bool>();
        
        // 创建并行查询并行处理结果
        Enumerable.Range(1, 1000)
            .AsParallel()
            .Select(i => 
            {
                // 记录当前线程ID
                int threadId = Thread.CurrentThread.ManagedThreadId;
                usedThreads[threadId] = true;
                
                // 模拟一些计算工作
                Thread.Sleep(1);
                return $"Item {i} processed on thread {threadId}";
            })
            // ForAll会并行处理每个结果项
            .ForAll(result => 
            {
                // 在处理结果的线程中继续进行操作
                // 注意:此线程可能与生成结果的线程不同
                int threadId = Thread.CurrentThread.ManagedThreadId;
                usedThreads[threadId] = true;
                
                // 将结果添加到线程安全集合
                resultBag.Add(result);
            });
        
        Console.WriteLine($"处理完成。使用的线程数: {usedThreads.Count}");
        Console.WriteLine($"处理的项目数: {resultBag.Count}");
    }
}

异常处理

并行编程中的异常处理需要特别注意,PLINQ提供了AggregateException来收集并行操作中的多个异常:

using System;
using System.Linq;

public class PlinqExceptionHandling
{
    public void HandleExceptionsInPlinq()
    {
        try
        {
            // 创建一个可能引发异常的并行查询
            var results = Enumerable.Range(0, 100)
                .AsParallel()
                .Select(i => 
                {
                    // 故意在某些情况下抛出异常
                    if (i % 10 == 0)
                        throw new DivideByZeroException($"除以零错误,索引: {i}");
                    
                    return 100 / (i % 10); // 当i%10=0时会抛出异常
                })
                .ToList(); // 这里会触发查询执行
        }
        catch (AggregateException ae)
        {
            Console.WriteLine($"捕获到{ae.InnerExceptions.Count}个异常:");
            
            foreach (var ex in ae.InnerExceptions)
            {
                Console.WriteLine($" - {ex.GetType().Name}: {ex.Message}");
            }
            
            // 也可以只处理特定类型的异常
            ae.Handle(ex => 
            {
                if (ex is DivideByZeroException)
                {
                    Console.WriteLine($"处理除零异常: {ex.Message}");
                    return true; // 表示此异常已处理
                }
                
                return false; // 表示此异常未处理
            });
        }
    }
}

并行LINQ的性能考量

PLINQ并不是所有情况下都能提供性能优势。以下是一些影响PLINQ性能的因素:

PLINQ性能因素
数据量
操作复杂度
数据结构
分区开销
合并开销
硬件环境
小数据集可能更慢
大数据集通常有优势
简单操作可能更慢
复杂操作通常有优势
线程安全性
内存布局
数据分割成本
结果合并成本
处理器核心数
内存带宽

异步LINQ vs 并行LINQ:如何选择?

异步LINQ和并行LINQ都是提高.NET应用程序性能的强大工具,但它们的设计目标和适用场景有着明显的区别。以下是一个详细的对比:

异步LINQ
Task-based Asynchronous Pattern
优化I/O密集型操作
提高响应性
减少线程阻塞
并行LINQ
Task Parallel Library
优化CPU密集型操作
提高吞吐量
利用多核并行处理
应用场景
数据特性
操作类型
响应需求
资源约束

功能对比

特性 异步LINQ 并行LINQ
主要目标 提高响应性 提高吞吐量
优化场景 I/O密集型操作 CPU密集型操作
关键技术 async/await 多线程并行
线程使用 释放当前线程 使用多个线程
适用数据源 远程数据、网络、文件 内存中的大型集合
编程模型 顺序但非阻塞 并行分解与合并
错误处理 单个异常 聚合异常
框架支持 EF Core, HttpClient等 System.Linq.Parallel

选择指南

  1. 当操作是I/O密集型且不需要并行处理时,选择异步LINQ

    • 从数据库读取数据
    • 调用Web API
    • 文件读写操作
    • 需要保持UI响应性
  2. 当操作是CPU密集型且数据量大时,选择并行LINQ

    • 复杂数学计算
    • 大集合的处理和转换
    • 图像处理
    • 需要最大化利用多核处理器
  3. 在某些情况下,可以组合使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class CombiningAsyncAndParallel
{
    public async Task<List<ProcessedData>> ProcessDataAsync(List<DataSource> sources)
    {
        // 异步获取所有数据
        var dataFetchTasks = sources.Select(source => FetchDataAsync(source));
        var allData = await Task.WhenAll(dataFetchTasks);
        
        // 数据合并后并行处理(CPU密集型操作)
        var flattenedData = allData.SelectMany(data => data).ToList();
        
        // 使用并行LINQ处理合并后的数据
        var processedResults = flattenedData
            .AsParallel()
            .WithDegreeOfParallelism(Environment.ProcessorCount)
            .Select(item => ProcessDataItem(item))
            .ToList();
            
        return processedResults;
    }
    
    // 异步获取数据(I/O密集型)
    private async Task<List<RawData>> FetchDataAsync(DataSource source)
    {
        // 模拟从远程源异步获取数据
        await Task.Delay(100); // 模拟网络延迟
        
        return new List<RawData>
        {
            new RawData { Id = 1, Value = $"Data from {source.Name}-1" },
            new RawData { Id = 2, Value = $"Data from {source.Name}-2" }
        };
    }
    
    // CPU密集型数据处理
    private ProcessedData ProcessDataItem(RawData data)
    {
        // 模拟一些CPU密集型处理
        System.Threading.Thread.Sleep(10);
        
        return new ProcessedData
        {
            Id = data.Id,
            ProcessedValue = data.Value.ToUpper(),
            ProcessedDate = DateTime.Now
        };
    }
    
    // 模型类
    public class DataSource
    {
        public string Name { get; set; }
        public string Url { get; set; }
    }
    
    public class RawData
    {
        public int Id { get; set; }
        public string Value { get; set; }
    }
    
    public class ProcessedData
    {
        public int Id { get; set; }
        public string ProcessedValue { get; set; }
        public DateTime ProcessedDate { get; set; }
    }
}

性能优化最佳实践

异步LINQ性能优化

  1. 避免异步过度使用

    • 不要为简单快速的操作添加异步,反而会引入开销
    • 尽量避免为极小的操作单独创建Task
  2. 使用Task.WhenAll并发调用

    • 当有多个独立的异步操作时,并发执行它们
    • 避免多个连续的await,使用Task.WhenAll提高并发性
// 不推荐的连续等待
var data1 = await GetDataAsync1();
var data2 = await GetDataAsync2();
var data3 = await GetDataAsync3();

// 推荐的并发执行
var task1 = GetDataAsync1();
var task2 = GetDataAsync2(); 
var task3 = GetDataAsync3();
var results = await Task.WhenAll(task1, task2, task3);
  1. 正确使用ConfigureAwait
    • 在库代码中使用ConfigureAwait(false)以避免不必要的上下文切换
    • 在UI应用程序中,视情况使用ConfigureAwait(true)以确保UI操作在正确线程上执行
// 在库代码中
public async Task<Data> LibraryMethodAsync()
{
    // 使用ConfigureAwait(false)避免回到捕获上下文
    var result = await FetchDataAsync().ConfigureAwait(false);
    return ProcessData(result);
}

// 在UI代码中
public async void ButtonClick_Handler()
{
    // 默认或使用ConfigureAwait(true)确保UI操作在UI线程上执行
    var result = await LibraryMethodAsync();
    UpdateUI(result); // 需要在UI线程上执行
}
  1. 选择适当的异步粒度

    • 太细粒度:增加Task创建和调度开销
    • 太粗粒度:可能阻塞线程过长
  2. 利用异步流和缓冲:

    • 处理大数据集时,使用IAsyncEnumerable流式处理
    • 考虑批处理以平衡延迟和吞吐量

并行LINQ性能优化

  1. 选择合适的场景

    • 当集合足够大、操作足够复杂时使用PLINQ
    • 通常数据量小于1000的简单操作反而可能因并行开销而更慢
  2. 控制并行度

    • 考虑硬件能力,适当设置.WithDegreeOfParallelism()
    • 大多数情况下,设置为处理器核心数量是个好选择
  3. 最小化分区和合并成本

    • 避免在并行操作中频繁使用需要同步的共享状态
    • 尽可能独立处理每个分区,最后才合并结果
  4. 注意查询运算符开销

    • AsOrdered()会增加开销,只在必要时使用
    • 考虑将WithMergeOptions()与不同的合并策略结合使用
  5. 使用正确的数据结构

    • 使用线程安全集合类型,如ConcurrentBagConcurrentDictionary
    • 避免在PLINQ中使用锁定,转而使用线程安全的操作
  6. 考虑负载均衡

    • 当工作负载不均匀时,考虑自定义分区策略
    • 使用.WithExecutionMode()指定执行模式

实际应用场景

异步LINQ应用场景

  1. Web API和前端应用
    • 异步加载数据,保持UI响应性
    • 处理用户请求,同时不阻塞web服务器线程
// 在ASP.NET Core控制器中
public class ProductsController : ControllerBase
{
    private readonly IProductRepository _repository;
    
    public ProductsController(IProductRepository repository)
    {
        _repository = repository;
    }
    
    [HttpGet]
    public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts([FromQuery] ProductFilter filter)
    {
        // 使用异步LINQ不会阻塞服务器线程
        var products = await _repository.GetProductsAsync()
            .Where(p => p.Category == filter.Category)
            .Skip((filter.Page - 1) * filter.PageSize)
            .Take(filter.PageSize)
            .Select(p => new ProductDto
            {
                Id = p.Id,
                Name = p.Name,
                Price = p.Price,
                // 更多属性映射...
            })
            .ToListAsync(); // 异步执行查询
        
        return Ok(products);
    }
}
  1. 大文件处理
    • 异步流式处理大型日志文件
    • 实时数据流处理
public class LogProcessor
{
    public async Task<LogSummary> ProcessLogFileAsync(string filePath)
    {
        var summary = new LogSummary();
        
        // 使用异步流读取大文件
        await foreach (var line in File.ReadLinesAsync(filePath))
        {
            if (string.IsNullOrWhiteSpace(line))
                continue;
                
            var logEntry = ParseLogLine(line);
            
            // 更新统计信息
            if (logEntry.Level == "ERROR")
                summary.ErrorCount++;
            else if (logEntry.Level == "WARNING")
                summary.WarningCount++;
            
            if (logEntry.ResponseTime > 1000)
                summary.SlowResponseCount++;
                
            summary.TotalRequests++;
        }
        
        return summary;
    }
    
    private LogEntry ParseLogLine(string line)
    {
        // 解析日志行的实现
        // ...
        return new LogEntry();
    }
    
    public class LogEntry
    {
        public string Level { get; set; }
        public DateTime Timestamp { get; set; }
        public int ResponseTime { get; set; }
        // 其他属性...
    }
    
    public class LogSummary
    {
        public int TotalRequests { get; set; }
        public int ErrorCount { get; set; }
        public int WarningCount { get; set; }
        public int SlowResponseCount { get; set; }
    }
}

并行LINQ应用场景

  1. 大数据分析
    • 海量数据聚合和统计
    • 复杂数据转换和处理
public class SalesAnalyzer
{
    public SalesReport AnalyzeSalesData(List<SalesTransaction> transactions)
    {
        var report = new SalesReport();
        
        // 使用并行LINQ进行数据分析
        var results = transactions
            .AsParallel()
            .GroupBy(t => new { t.Region, t.ProductCategory })
            .Select(g => new RegionalSalesSummary
            {
                Region = g.Key.Region,
                Category = g.Key.ProductCategory,
                TotalSales = g.Sum(t => t.Amount),
                AverageOrder = g.Average(t => t.Amount),
                TransactionCount = g.Count(),
                HighValueOrders = g.Count(t => t.Amount > 1000)
            })
            .ToList();
        
        report.RegionalSummaries = results;
        report.TotalSalesAmount = results.Sum(r => r.TotalSales);
        report.GeneratedAt = DateTime.Now;
        
        return report;
    }
    
    public class SalesTransaction
    {
        public string Region { get; set; }
        public string ProductCategory { get; set; }
        public decimal Amount { get; set; }
        public DateTime Date { get; set; }
    }
    
    public class RegionalSalesSummary
    {
        public string Region { get; set; }
        public string Category { get; set; }
        public decimal TotalSales { get; set; }
        public decimal AverageOrder { get; set; }
        public int TransactionCount { get; set; }
        public int HighValueOrders { get; set; }
    }
    
    public class SalesReport
    {
        public List<RegionalSalesSummary> RegionalSummaries { get; set; }
        public decimal TotalSalesAmount { get; set; }
        public DateTime GeneratedAt { get; set; }
    }
}
  1. 图像处理
    • 批量图像调整和转换
    • 图像分析和特征提取
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

public class ImageProcessor
{
    public void BatchProcessImages(string inputDirectory, string outputDirectory)
    {
        // 确保输出目录存在
        Directory.CreateDirectory(outputDirectory);
        
        // 获取所有图像文件
        var imageFiles = Directory.GetFiles(inputDirectory, "*.jpg")
            .Concat(Directory.GetFiles(inputDirectory, "*.png"))
            .ToList();
        
        // 并行处理所有图像
        imageFiles.AsParallel()
            .ForAll(filePath => 
            {
                try
                {
                    // 加载图像
                    using var originalImage = Image.FromFile(filePath);
                    
                    // 处理图像 - 调整大小、应用滤镜等
                    using var processedImage = ResizeImage(originalImage, 800, 600);
                    
                    // 构建输出路径
                    string fileName = Path.GetFileName(filePath);
                    string outputPath = Path.Combine(outputDirectory, "processed_" + fileName);
                    
                    // 保存处理后的图像
                    processedImage.Save(outputPath);
                    
                    Console.WriteLine($"已处理: {fileName}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"处理 {filePath} 时出错: {ex.Message}");
                }
            });
    }
    
    private Image ResizeImage(Image image, int width, int height)
    {
        // 创建调整大小后的位图
        var destRect = new Rectangle(0, 0, width, height);
        var destImage = new Bitmap(width, height);
        
        // 设置分辨率
        destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
        
        // 绘制调整大小的图像
        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
            graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            
            graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
        }
        
        return destImage;
    }
}

结论

异步LINQ和并行LINQ代表了.NET中两种不同的性能优化方法,针对不同类型的操作和场景:

  • 异步LINQ让应用程序在等待I/O操作完成时保持响应性,通过释放线程而不是阻塞线程来提高效率。这对于Web应用、桌面UI应用和任何需要处理远程数据的场景尤为重要。

  • 并行LINQ通过在多个核心上并行执行计算密集型任务来提高吞吐量。当数据量大且每个数据项的处理需要大量CPU计算时,PLINQ可以显著缩短总处理时间。

要有效使用这些技术,需要理解应用程序的瓶颈在哪里:

  • 如果瓶颈是I/O等待时间,异步LINQ是更好的选择。
  • 如果瓶颈是CPU处理能力,并行LINQ可能提供更好的性能。

在某些复杂场景中,合理结合这两种技术可以获得最佳性能。记住,高级性能优化应该基于实际测量和性能分析,而不仅仅是理论假设。使用诸如BenchmarkDotNet这样的工具来测量不同实现的实际性能差异,然后做出明智的决策。

无论选择哪种方法,都应关注代码可读性和可维护性。性能改进不应以牺牲代码质量为代价。好的异步和并行代码不仅执行更快,而且仍然应该清晰、可读且易于维护。

学习资源

  1. Microsoft Docs: 异步编程
  2. Microsoft Docs: 并行LINQ (PLINQ)
  3. Microsoft Docs: Task Parallel Library (TPL)
  4. Entity Framework Core 异步操作
  5. .NET 中的异步流

你可能感兴趣的:(LINQ,C#,linq,.net,solr)