跟着AI学习C# Day26

📅 Day 26:C# 异步编程进阶

✅ 学习目标:

  • 深入理解 async/await 的底层机制;
  • 掌握 ConfigureAwait(false) 的作用与使用场景;
  • 避免异步死锁,理解同步上下文(Synchronization Context);
  • 掌握并行任务处理技巧(Parallel, PLINQ);
  • 使用 ValueTask 替代 Task 提升性能;
  • 构建高性能的异步 API;
  • 编写一个结合多个异步优化技巧的示例程序(如高并发网络爬虫)。

🧠 一、回顾 async/await 基础知识

asyncawait 是什么?

  • async 标记方法为异步方法;
  • await 用于等待异步操作完成而不阻塞线程。
csharp 复制代码
public async Task<string> DownloadAsStringAsync(string url)
{
    using var client = new HttpClient();
    return await client.GetStringAsync(url);
}

✅ 状态机原理简述:

编译器会将 async 方法转换为状态机对象(IAsyncStateMachine),自动管理异步流程和上下文切换。


🔁 二、ConfigureAwait(false) 的意义与使用

✅ 默认行为(不加 ConfigureAwait(false)

在 UI 应用中(如 WPF、WinForms),默认会捕获当前的 SynchronizationContext,以便 await 后继续回到 UI 线程执行后续代码。

❗️问题:可能导致死锁!

如果你在 UI 线程调用了 .Result.Wait(),而异步方法又试图回到 UI 线程,就会发生死锁。

✅ 正确做法:库方法应使用 ConfigureAwait(false)

csharp 复制代码
public async Task<string> GetDataAsync()
{
    string result = await SomeNetworkCallAsync().ConfigureAwait(false);
    return Process(result);
}

⚠️ 在类库中始终使用 ConfigureAwait(false),除非你明确需要返回到特定上下文(如 UI)。


💀 三、避免异步死锁

❌ 错误写法(UI 线程中):

csharp 复制代码
var result = GetDataAsync().Result;

这会导致主线程被阻塞,并且 await 后想回到这个线程,但该线程正等着结果,导致死锁。

✅ 正确写法:

csharp 复制代码
var result = await GetDataAsync();

或者,在非 UI 场景中使用:

csharp 复制代码
Task.Run(async () => await GetDataAsync()).Wait();

🧩 四、并行任务处理(Parallel & PLINQ)

Parallel.For / Parallel.ForEach

适用于 CPU 密集型任务的并行执行。

csharp 复制代码
Parallel.For(0, 10, i =>
{
    Console.WriteLine($"处理 {i},线程ID:{Thread.CurrentThread.ManagedThreadId}");
});

✅ PLINQ(Parallel LINQ)

并行查询,适合大数据集合处理:

csharp 复制代码
var numbers = Enumerable.Range(1, 1000000);

var result = numbers.AsParallel()
                    .Where(n => n % 3 == 0)
                    .Sum();

Console.WriteLine("总和:" + result);

🧱 五、ValueTask vs Task(.NET Core 2.1+)

Task<T> 的缺点:

每次调用都会分配内存(堆上创建对象),对高频调用或热路径有性能影响。

ValueTask<T> 的优势:

  • 如果结果已知(缓存命中、立即完成),则不分配;
  • 适用于"大多数快速完成"的异步操作。
csharp 复制代码
public ValueTask<int> GetCachedValueAsync()
{
    if (_cache.HasValue)
        return new ValueTask<int>(_cache.Value);
    else
        return new ValueTask<int>(GetValueFromNetworkAsync());
}

✅ 注意:不能多次 await,否则可能抛出异常。


🔄 六、自定义异步状态机(高级)

你可以通过实现 IValueTaskSource<TResult> 来构建自己的 ValueTask 实现,但这通常只在高性能框架开发中使用。

示例略(复杂度较高,需深入理解状态机机制)。


🧪 七、实战练习:高并发网页爬虫

功能要求:

  • 并发下载多个网页;
  • 使用 HttpClient 异步请求;
  • 避免死锁;
  • 使用 ValueTask 缓存热门页面;
  • 支持配置最大并发数;
  • 输出各页面大小。
示例代码框架:
csharp 复制代码
class WebCrawler
{
    private readonly HttpClient _client = new();
    private readonly ConcurrentDictionary<string, string> _cache = new();

    public async Task<int> CrawlPageAsync(string url)
    {
        // 使用缓存
        if (_cache.TryGetValue(url, out var cached))
            return cached.Length;

        // 下载网页
        string content = await _client.GetStringAsync(url).ConfigureAwait(false);
        _cache.TryAdd(url, content);

        return content.Length;
    }

    public async Task RunAsync(IEnumerable<string> urls)
    {
        var tasks = urls.Select(url =>
            Task.Run(async () =>
            {
                int length = await CrawlPageAsync(url).ConfigureAwait(false);
                Console.WriteLine($"{url} 长度:{length}");
            }));

        await Task.WhenAll(tasks);
    }
}

📝 小结

今天你学会了:

  • ConfigureAwait(false) 的作用与使用时机;
  • 如何避免异步死锁;
  • 使用 ParallelPLINQ 实现并行任务;
  • ValueTask 的优势及其适用场景;
  • 自定义异步状态机的基本概念;
  • 编写了一个高并发网页爬虫的异步优化示例。

掌握这些高级异步编程技巧,能显著提升应用程序的响应性、吞吐量和资源利用率,尤其在 Web API、微服务、桌面应用等场景中尤为重要。


🧩 下一步学习方向(Day 27)

明天我们将进入一个新的主题 ------ C# 中的反射(Reflection)与元编程,你将学会:

  • 如何在运行时动态加载类型、调用方法;
  • 获取类成员信息(属性、方法、构造函数);
  • 使用 System.Reflection.Emit 创建动态程序集;
  • 反射在依赖注入、序列化、ORM 等框架中的应用;
  • 性能优化技巧(缓存反射结果、使用表达式树代替 Invoke);
  • 编写一个基于反射的通用对象克隆器。
相关推荐
胖咕噜的稞达鸭27 分钟前
自定义shell命令行解释器自制
java·开发语言
草莓熊Lotso27 分钟前
Git 分支管理:从基础操作到协作流程(本地篇)
大数据·服务器·开发语言·c++·人工智能·git·sql
报错小能手30 分钟前
C++异常处理 终极及总结
开发语言·c++
AI绘画哇哒哒2 小时前
【收藏必看】大模型智能体六大设计模式详解:从ReAct到Agentic RAG,构建可靠AI系统
人工智能·学习·ai·语言模型·程序员·产品经理·转行
小奶包他干奶奶6 小时前
Webpack学习——Loader(文件转换器)
前端·学习·webpack
小奶包他干奶奶6 小时前
Webpack学习——原理理解
学习·webpack·devops
励志成为美貌才华为一体的女子6 小时前
强化学习PPO和GRPO逻辑学习
学习
tobebetter95276 小时前
How to manage python versions on windows
开发语言·windows·python
meichaoWen7 小时前
【Vue3】vue3的全面学习(一)
前端·javascript·学习
FFF团团员9097 小时前
树莓派学习笔记3:LED和Button
笔记·学习