前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >在 .Net Core 中使用 Task.WhenAll 提高 UI 性能

在 .Net Core 中使用 Task.WhenAll 提高 UI 性能

作者头像
郑子铭
发布2025-01-07 10:50:59
发布2025-01-07 10:50:59
10300
代码可运行
举报
运行总次数:0
代码可运行

Task.WhenAll可以通过并行而不是顺序运行任务来显著提高应用程序的性能。

这对于网络请求、文件I/O和数据库查询等I/O密集型操作特别有用。

使用Task.WhenAll的性能优势 使用Task.WhenAll通过以下方式改善性能:

  1. 最大化资源利用: 并发运行的任务可以更好地利用CPU和IO资源,从而加快整体执行速度。
  2. 减少等待时间: Task.WhenAll允许多个任务同时运行,而不是等待一个任务完成后再启动下一个,从而减少总等待时间。
  3. 避免线程阻塞: 使用Task.WhenAll的异步编程可以防止线程阻塞,使应用程序更具响应性和可扩展性。
  4. 提高吞吐量: 并行运行任务可以提高应用程序的吞吐量,使其能够在更短的时间内处理更多工作。

示例场景: 假设你有三个任务,每个任务需要2秒钟完成。如果按顺序运行,总时间将为6秒。但是,如果使用Task.WhenAll并行运行它们,假设没有资源争用问题,总时间将只需2秒。

Task.WhenAll是.NET Core中任务并行库(TPL)提供的一个方法,它用于创建一个任务,该任务在所有提供的任务完成时完成。在需要并行运行多个异步操作并等待所有操作完成后再继续的场景中特别有用。

让我们讨论实际场景

  1. 并发执行多个数据库查询 假设我们有n个SQL查询,我们想并行执行,那么我们可以使用Task.WhenAll。

在这种情况下,UI将比按顺序执行这些SQL获得更快的响应。

代码语言:javascript
代码运行次数:0
运行
复制
using System.Data.SqlClient;
usingSystem.Threading.Tasks;

publicclassDatabaseService
{
    privatestring connectionString ="your_connection_string";

    publicasyncTaskExecuteQueriesAsync(string[] queries)
    {
        var tasks = queries.Select(query =>ExecuteQueryAsync(query)).ToArray();
        await Task.WhenAll(tasks);
    }

    privateasyncTaskExecuteQueryAsync(string query)
    {
        using(var connection =newSqlConnection(connectionString))
        {
            await connection.OpenAsync();
            using(var command =newSqlCommand(query, connection))
            {
                await command.ExecuteNonQueryAsync();
            }
        }
    }
}
  1. 并发执行多个I/O操作 假设我们需要并行读取多个文件,那么我们可以使用Task.WhenAll。

考虑以下代码:

代码语言:javascript
代码运行次数:0
运行
复制
using System.IO;
usingSystem.Threading.Tasks;

publicclassIoService
{
    publicasyncTaskReadFiles(string[] filePaths)
    {
        var tasks = filePaths.Select(filePath => Task.Run(()=> File.ReadAllText(filePath))).ToArray();
        var results =await Task.WhenAll(tasks);

        foreach(var content in results)
        {
            Console.WriteLine(content);
        }
    }
}

我们正在并行读取文件,UI将快速获得响应。

  1. 不同端点的并发API调用 假设我们想并行读取多个API。

我们使用HttpClient对象与端点建立连接。

代码语言:javascript
代码运行次数:0
运行
复制
using System.Net.Http;
usingSystem.Threading.Tasks;

publicclassApiService
{
    privatestaticreadonlyHttpClient httpClient =newHttpClient();

    publicasyncTaskFetchDataFromEndpointsAsync(string[] endpoints)
    {
        var tasks = endpoints.Select(endpoint => httpClient.GetStringAsync(endpoint)).ToArray();
        var results =await Task.WhenAll(tasks);

        foreach(var result in results)
        {
            Console.WriteLine(result);
        }
    }
}
  1. 并发下载多个文件 假设我们在数据库、Blob存储或某个文件系统中有多个文件,我们想独立下载,那么我们可以在这里使用Task.WhenAll。

优点:与顺序下载相比,并发下载文件可以显著减少总下载时间。

代码语言:javascript
代码运行次数:0
运行
复制
using System.Net.Http;
usingSystem.Threading.Tasks;

publicclassDownloadService
{
    privatestaticreadonlyHttpClient httpClient =newHttpClient();

    publicasyncTaskDownloadFilesAsync(string[] urls,string destinationFolder)
    {
        var tasks = urls.Select(url =>DownloadFileAsync(url, destinationFolder)).ToArray();
        await Task.WhenAll(tasks);
    }

    privateasyncTaskDownloadFileAsync(string url,string destinationFolder)
    {
        var fileName = Path.Combine(destinationFolder, Path.GetFileName(url));
        var data =await httpClient.GetByteArrayAsync(url);
        await File.WriteAllBytesAsync(fileName, data);
    }
}
  1. 执行多个后台任务

场景:应用程序需要执行多个独立的后台任务,如数据清理、日志记录和报告生成。

优点:并发运行后台任务确保它们更快完成,使应用程序能够更快地处理新任务。

代码语言:javascript
代码运行次数:0
运行
复制
using System.Threading.Tasks;

publicclassBackgroundTaskService
{
    publicasyncTaskRunBackgroundTasksAsync()
    {
        var tasks =newTask[]
        {
            Task.Run(()=>CleanupData()),
            Task.Run(()=>LogActivity()),
            Task.Run(()=>GenerateReports())
        };

        await Task.WhenAll(tasks);
    }

    privatevoidCleanupData()
    {
        // Data cleanup logic
    }

    privatevoidLogActivity()
    {
        // Logging logic
    }

    privatevoidGenerateReports()
    {
        // Reporting logic
    }
}

Task.WhenAll的异常处理 使用Task.WhenAll时,正确处理异常很重要,因为任何任务都可能失败,你需要一种方法来管理这些失败。如果任何任务失败,Task.WhenAll本身将抛出AggregateException。以下是如何以健壮的方式处理异常。

Task.WhenAll的异常处理 关键点:

  1. AggregateException: 当Task.WhenAll完成时,如果任何任务失败,它会抛出一个AggregateException。这个异常包含所有任务抛出的个别异常。
  2. 处理单个任务异常: 你可以遍历AggregateException的InnerExceptions属性来处理每个单独的异常。
  3. 使用ContinueWith进行异常处理: 使用ContinueWith在异常发生时立即处理。

示例1:Task.WhenAll的基本异常处理 这是一个演示如何处理使用Task.WhenAll时异常的简单示例:

代码语言:javascript
代码运行次数:0
运行
复制
using System;
usingSystem.Linq;
usingSystem.Threading.Tasks;

publicclassExceptionHandlingService
{
    publicasyncTaskProcessTasksWithExceptionHandlingAsync()
    {
        var tasks =newTask[]
        {
            Task.Run(()=>PerformTask()),
            Task.Run(()=>PerformTask()),
            Task.Run(()=>PerformTask())
        };

        try
        {
            await Task.WhenAll(tasks);
        }
        catch(AggregateException ex)
        {
            foreach(var innerException in ex.InnerExceptions)
            {
                Console.WriteLine($"Task failed with: {innerException.Message}");
            }
        }
    }

    privatevoidPerformTask(int taskNumber)
    {
        if(taskNumber ==)
        {
            thrownewInvalidOperationException($"Task {taskNumber} encountered an error.");
        }
        Console.WriteLine($"Task {taskNumber} completed successfully.");
    }
}

publicclassProgram
{
    publicstaticasyncTaskMain(string[] args)
    {
        var service =newExceptionHandlingService();
        await service.ProcessTasksWithExceptionHandlingAsync();
    }
}

示例2:使用ContinueWith立即处理异常 这个示例演示了如何使用ContinueWith在异常发生时立即处理:

代码语言:javascript
代码运行次数:0
运行
复制
using System;
usingSystem.Linq;
usingSystem.Threading.Tasks;

publicclassImmediateExceptionHandlingService
{
    publicasyncTaskProcessTasksWithImmediateExceptionHandlingAsync()
    {
        var tasks =newTask[]
        {
            Task.Run(()=>PerformTask()).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted),
            Task.Run(()=>PerformTask()).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted),
            Task.Run(()=>PerformTask()).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted)
        };

        try
        {
            await Task.WhenAll(tasks);
        }
        catch(AggregateException ex)
        {
            foreach(var innerException in ex.InnerExceptions)
            {
                Console.WriteLine($"Task failed with: {innerException.Message}");
            }
        }
    }

    privatevoidPerformTask(int taskNumber)
    {
        if(taskNumber ==)
        {
            thrownewInvalidOperationException($"Task {taskNumber} encountered an error.");
        }
        Console.WriteLine($"Task {taskNumber} completed successfully.");
    }

    privatevoidHandleException(Task task)
    {
        if(task.Exception !=null)
        {
            foreach(var ex in task.Exception.InnerExceptions)
            {
                Console.WriteLine($"Handled immediately: {ex.Message}");
            }
        }
    }
}

publicclassProgram
{
    publicstaticasyncTaskMain(string[] args)
    {
        var service =newImmediateExceptionHandlingService();
        await service.ProcessTasksWithImmediateExceptionHandlingAsync();
    }
}

说明

  1. 基本异常处理: 在第一个示例中,Task.WhenAll在try-catch块中被等待。如果任何任务抛出异常,它将在catch块中被捕获,并且每个InnerException都会被单独处理。
  2. 使用ContinueWith的即时异常处理: 在第二个示例中,ContinueWith与TaskContinuationOptions.OnlyOnFaulted选项一起使用,以在异常发生后立即处理。这允许你在任务失败时立即记录或处理异常,同时仍然使用Task.WhenAll等待所有任务完成。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档