01-并发编程全景图 [复制链接]
发表于 前天 02:47 | 显示全部楼层 |阅读模式
01. 并发编程全景图:为什么你的代码又慢又卡?

从一个真实的故事开始
你刚写完一个 ASP.NET Core API,当地测试飞快。摆设上线后,10 个并发用户就能把服务器 CPU 打满,相应时间从 100ms 飙到 5 秒。你懵了:代码没标题啊,为什么性能这么差?
标题的根源,极大概率就藏在并发、并行、异步这三个概念里。搞懂它们,你的代码性能能提拔 10-100 倍
💡 配套代码:本章全部代码示例都可以在 Overview 项目中运行。
📁 项目结构

  • ConcurrencyDemo.cs - 并发示例(做饭场景)
  • ParallelDemo.cs - 并行示例(多核盘算)
  • AsyncDemo.cs - 异步示例(模仿下载)
  • TaskTypeDemo.cs - 使命范例辨认
  • CommonMistakesDemo.cs - 常见误区
🤔 第一个标题:为什么这三个概念这么容易肴杂?

在讨论详细技能之前,我们先搞清晰一个根本标题:为什么并发、并行、异步这么容易肴杂?
答案是:它们都在形貌"同时做多件事",但角度完全差别
想象你在看一场足球比赛:

  • 并发(Concurrency):同一台摄像机在快速切换差别的视角(球员、锻练、观众),你感觉是"同时"看到了全部画面,但现实上是快速切换
  • 并行(Parallelism):有 10 个摄像机真正同时拍摄差别的角度
  • 异步(Asynchronous):你预约了比赛录像,不消不停盯着电视等,体系会在录制完成后关照你
这三个概念的共同点是都在处理惩罚"多使命",区别在于:

  • 并发关注逻辑结构(怎么构造代码)
  • 并行关注物理实行(用几个 CPU 核心)
  • 异步关注期待方式(壅闭还优劣壅闭)
📌 概念深度分析:不但是界说,更要明白本质

1.1 并发(Concurrency):步伐员的头脑方式

官方界说听起来总是很抽象。让我换个方式说:
并发是你构造代码的方式,让步伐可以或许"处理惩罚"多个使命,而不管这些使命是不是真的同时实行。
为什么必要并发?

现实天下本身就是并发的:

  • 你的 Web 服务器要同时处理惩罚 1000 个哀求
  • 你的桌面步伐要同时相应用户点击、更新界面、下载文件
  • 你的游戏要同时处理惩罚物理盘算、AI、渲染、音效
假如用单线程串行处理惩罚,用户体验会瓦解。
并发的本质:使命切换

关键洞察:单核 CPU 一次只能实行一条指令,但为什么你感觉电脑在"同时"运行 100 个步伐?
答案是时间片轮转
  1. 时间轴 →
  2. [任务A][任务B][任务C][任务A][任务B][任务C]...
  3. 10ns   10ns  10ns  10ns  10ns  10ns
复制代码
切换得富足快,人类就感觉不出来了(人眼辨认延长约 100ms)。
代码示例:并发做饭

这是一个经典的并发场景。留意观察线程 ID
  1. // 来自 ConcurrencyDemo.cs
  2. private static async Task ConcurrentCookingAsync()
  3. {
  4.     Console.WriteLine("开始做饭(并发模式)");
  5.     // 启动三个异步任务
  6.     var task1 = StirFryAsync();   // 炒菜
  7.     var task2 = MakeSoupAsync();   // 煮汤
  8.     var task3 = SteamRiceAsync();  // 蒸米饭
  9.     // 等待所有任务完成
  10.     await Task.WhenAll(task1, task2, task3);
  11.     Console.WriteLine("所有菜都做好了!");
  12. }
复制代码
运行后你会发现:全部使命大概都在同一个线程上完成!这就是并发的魔力。
💡 运行示例:dotnet run --project Overview 观察线程 ID
深入思考:并发 ≠ 快

紧张认知:并发不是为了"快",而是为了不浪费时间
做饭时,炒菜必要等油热(I/O 期待),这段时间你可以去切菜(使命切换)。并发让你充实利用期待时间,而不是傻站着。
1.2 并行(Parallelism):硬件的暴力美学

假如说并发是"奇妙地切换",那并行就是"真刀真枪地同时干"。
并行的硬件底子

当代 CPU 都是多核的(4 核、8 核、16 核)。每个核心都是一个独立的盘算单元,能真正同时实行指令。
  1. CPU 核心 1: [计算质数] [计算质数] [计算质数]...
  2. CPU 核心 2: [计算质数] [计算质数] [计算质数]...
  3. CPU 核心 3: [计算质数] [计算质数] [计算质数]...
  4. CPU 核心 4: [计算质数] [计算质数] [计算质数]...
复制代码
什么时间必要并行?

只有一种情况:CPU 麋集型使命。
什么是 CPU 麋集型?就是不必要期待外部资源,纯靠 CPU 盘算的使命

  • 图像处理惩罚(每个像素都要盘算)
  • 视频编码(海量数据压缩)
  • 科学盘算(模仿、求解方程)
  • 数据分析(筛选、聚合)
代码示例:并行盘算质数
  1. // 来自 ParallelDemo.cs
  2. private static void ParallelProcessing()
  3. {
  4.     Console.WriteLine($"CPU 核心数: {Environment.ProcessorCount}");
  5.     Console.WriteLine("开始并行计算阶乘...");
  6.     var numbers = Enumerable.Range(1, 8).ToArray();
  7.     // 使用 Parallel.ForEach 并行处理
  8.     Parallel.ForEach(numbers, number =>
  9.     {
  10.         var threadId = Environment.CurrentManagedThreadId;
  11.         var result = ComputeFactorial(number);
  12.         Console.WriteLine($"  [Thread {threadId}] {number}! = {result}");
  13.     });
  14. }
复制代码
运行后你会看到:多个差别的线程 ID,它们在真正同时盘算!
💡 运行示例:观察线程 ID 的厘革,明白"真正同时"的寄义
并行的陷阱:Amdahl 定律

暴虐的现实:并行不是 4 核就快 4 倍。
缘故原由有三:

  • 线程创建开销:创建和烧毁线程必要时间
  • 上下文切换:CPU 在线程间切换必要生存/规复状态
  • 数据同步:多线程访问共享数据必要加锁(背面章节详解)
现实加速比通常是 2.5-3.5 倍,已经很不错了。
1.3 异步(Asynchronous):不傻等的艺术

这是最容易被误解的概念,也是当代 .NET 开辟的核心。
为什么必要异步?

想象你在餐厅点餐:
同步方式(壅闭)
  1. 你:我要一份牛排
  2. 服务员:好的(站在厨房门口等 20 分钟)
  3. 你:……(也在桌子旁干等)
  4. [20 分钟后]
  5. 服务员:您的牛排好了
复制代码
异步方式(非壅闭)
  1. 你:我要一份牛排
  2. 服务员:好的,请稍等,牛排好了我叫您(转身去服务其他客人)
  3. 你:……(可以刷手机、聊天)
  4. [20 分钟后]
  5. 服务员:先生,您的牛排好了
复制代码
异步的核心:在期待期间,去做其他事变
异步的硬件底子:I/O 完成端口

许多人不知道,异步操纵在期待期间不占用线程
当你调用 await httpClient.GetAsync() 时:

  • 发起网络哀求(占用线程,非常快,几微秒)
  • 线程立刻开释,行止理惩罚其他哀求
  • 期待网络相应(不占用线程,这是最耗时的阶段)
  • 网卡收到数据后,触发硬件停止
  • 操纵体系关照 .NET 运行时
  • .NET 从线程池取一个线程继承实行后续代码
关键点:在步调 3(期待相应)期间,没有线程在傻等!
代码示例:异步下载
  1. // 来自 AsyncDemo.cs
  2. private static async Task SimulateDownloadAsync(string fileName, int delayMs)
  3. {
  4.     var startThread = Environment.CurrentManagedThreadId;
  5.     Console.WriteLine($"  [Thread {startThread}] 开始下载 {fileName}...");
  6.     // 模拟异步 I/O 操作(等待期间线程被释放)
  7.     await Task.Delay(delayMs);
  8.     var endThread = Environment.CurrentManagedThreadId;
  9.     Console.WriteLine($"  [Thread {endThread}] {fileName} 下载完成 ✓");
  10.     // 注意:控制台应用中,await 后可能在同一线程恢复(线程池优化)
  11.     // 在 ASP.NET Core 中,通常会在不同线程恢复
  12.     if (startThread != endThread)
  13.     {
  14.         Console.WriteLine($"  → 线程切换:{startThread} → {endThread}");
  15.     }
  16.     else
  17.     {
  18.         Console.WriteLine($"  → 线程复用:线程池优化,复用了 Thread {startThread}");
  19.     }
  20. }
复制代码
运行后你会发现

  • 控制台应用:大概在同一线程完成(线程池优化)
  • ASP.NET Core:通常在差别线程规复(有 SynchronizationContext)
  • 关键点:无论是否切换线程,期待期间线程都被开释了!
💡 运行示例:观察线程举动
异步的威力:ASP.NET Core 案例

假设你有 100 个线程池线程(默认值),每个哀求必要调用数据库(耗时 50ms):
同步方式

  • 100 个线程同时处理惩罚 100 个哀求
  • 每个线程壅闭 50ms 期待数据库
  • 第 101 个哀求被拒绝(没有空闲线程)
异步方式

  • 100 个线程发起 100 个数据库哀求,立刻开释
  • 这 100 个线程可以继承处理惩罚新的哀求
  • 理论上可以同时处理惩罚数千个哀求
性能提拔:10-100 倍
1.4 三者关系:一个同一的视角


核心洞察

  • 并发是概念,并行和异步是实现方式
  • 并行办理盘算瓶颈,异步办理期待浪费
  • 它们可以组合利用(比如并行下载 100 个文件)
🔧 .NET 并发技能栈:为什么计划成如许?

许多文章只告诉你"有哪些工具",但不告诉你"为什么要有这些工具"。让我们换个角度。
2.1 演进史:从 Thread 到 async/await

阶段 1:原始期间 - Thread(.NET 1.0-3.5)
  1. // 2002 年,你是这样写并发代码的
  2. var thread = new Thread(() =>
  3. {
  4.     // 下载文件
  5.     var data = DownloadFile(url);
  6. });
  7. thread.Start();
  8. thread.Join(); // 阻塞等待
复制代码
标题

  • 创建线程开销大(1MB+ 栈空间)
  • 手动管理生命周期(忘记 Join 导致内存走漏)
  • 1000 个并发 = 1000 个线程 = 1GB+ 内存
阶段 2:线程池期间 - ThreadPool(.NET 2.0+)
  1. // 2005 年,微软引入线程池
  2. ThreadPool.QueueUserWorkItem(_ =>
  3. {
  4.     var data = DownloadFile(url);
  5. });
复制代码
改进

  • 复用线程,制止重复创建
  • 主动管理线程数量
标题

  • 回调地狱(Callback Hell)
  • 错误处理惩罚复杂
  • 无法获取返回值
阶段 3:使命期间 - Task(.NET 4.0)
  1. // 2010 年,Task 横空出世
  2. var task = Task.Run(() => DownloadFile(url));
  3. var data = task.Result; // 可以获取返回值了!
复制代码
改进

  • 同一的异步模子
  • 支持组合(Task.WhenAll)
  • 非常传播机制
标题

  • 仍然是回调风格
  • 代码可读性差
阶段 4:当代异步 - async/await(.NET 4.5+)
  1. // 2012 年,async/await 改变世界
  2. var data = await DownloadFileAsync(url);
  3. // 看起来像同步代码,实际是异步执行!
复制代码
革命性改进

  • 同步风格的异步代码(编译器状态机)
  • 主动非常传播
  • 完善的组合性
这就是为什么如今保举 async/await!
2.2 技能栈全景:每一层的存在意义


关键洞察

  • 越往上越"业务化",越往下越"体系化"
  • 大多数开辟者只必要关注异步编程层并行编程层
  • 同步原语层是"须要之恶"(后续章节详解)
🎯 场景辨认:怎样做出准确的技能选择?

这是最实用的部门。许多开辟者不是不知道工具,而是不知道什么时间用哪个工具
3.1 核心判定标准:使命在等什么?

一个简朴的标题就能决定齐备:你的代码在等什么?
  1. 任务在等什么?
  2.      │
  3.      ├─ 等 CPU 计算 ────→ CPU 密集型 ────→ 使用并行(Parallel/PLINQ)
  4.      │                                   
  5.      │
  6.      ├─ 等 I/O 完成 ────→ I/O 密集型 ────→ 使用异步(async/await)
  7.      │                                   
  8.      │
  9.      └─ 两者都有 ───────→ 混合型 ────────→ 组合使用
复制代码
3.2 CPU 麋集型:怎样辨认?

特性(满意恣意一条):

  • ✅ CPU 利用率 > 70%
  • ✅ 代码里有大量循环、递归、数学运算
  • ✅ 实行时间与 CPU 主频成反比
典范场景
  1. // ❌ 错误:用异步处理 CPU 密集任务
  2. public async Task<int[]> FindPrimesAsync(int max)
  3. {
  4.     return await Task.Run(() =>  // 这里的 Task.Run 是对的!
  5.     {
  6.         return Enumerable.Range(2, max)
  7.             .Where(IsPrime)  // CPU 密集计算
  8.             .ToArray();
  9.     });
  10. }
复制代码
准确做法(来自 TaskTypeDemo.cs):
  1. // ✅ 正确:使用 PLINQ
  2. private static void DemonstrateCpuBoundTask()
  3. {
  4.     var data = Enumerable.Range(1, 1_000_000).ToArray();
  5.     // 使用 PLINQ 并行处理
  6.     var primes = data
  7.         .AsParallel()      // 魔法在这里!
  8.         .Where(IsPrime)
  9.         .ToArray();
  10.     Console.WriteLine($"找到 {primes.Length} 个质数");
  11. }
复制代码
性能提拔:4 核 CPU 上约 2.5-3.5 倍
💡 运行示例:dotnet run --project Overview 观察耗时
3.3 I/O 麋集型:最容易踩坑的地方

特性(满意恣意一条):

  • ✅ CPU 利用率 < 30%
  • ✅ 代码在等网络、磁盘、数据库
  • ✅ 实行时间与网络延长成正比
常见错误(90% 的性能标题都是这个):
  1. // ❌ 错误:用 Task.Run 包装 I/O 操作
  2. public async Task<string> GetDataAsync()
  3. {
  4.     return await Task.Run(async () =>  // ❌ 画蛇添足!
  5.     {
  6.         using var client = new HttpClient();
  7.         return await client.GetStringAsync(url);  // I/O 操作
  8.     });
  9. }
复制代码
为什么错?

  • HttpClient.GetStringAsync 已经是异步的(不占用线程)
  • Task.Run 额外占用一个线程池线程
  • 这个线程在干什么?傻等网络相应
准确做法
  1. // ✅ 正确:直接 await
  2. public async Task<string> GetDataAsync()
  3. {
  4.     using var client = new HttpClient();
  5.     return await client.GetStringAsync(url);
  6.     // 等待期间,线程被释放去处理其他请求
  7. }
复制代码
💡 运行示例:检察 CommonMistakesDemo.cs 的性能对比
性能影响:在高并发下,吞吐量差异可达 10-100 倍
这个标题非常的典范,大部门人会在这里踩坑,背面的章节会偏重解说缘故原由
3.4 混淆型使命:组合的艺术

真实天下的使命每每是混淆的。关键是辨认每个步调的范例
案例:批量下载并处理惩罚图片
  1. // 来自 TaskTypeDemo.cs(改进版)
  2. public async Task ProcessImagesAsync(string[] urls)
  3. {
  4.     // 步骤 1:I/O 密集 - 并发下载(异步)
  5.     var downloadTasks = urls.Select(url => DownloadImageAsync(url));
  6.     var images = await Task.WhenAll(downloadTasks);
  7.    
  8.     // 步骤 2:CPU 密集 - 并行处理(Parallel)
  9.     var processed = new ConcurrentBag<Image>();
  10.     Parallel.ForEach(images, image =>
  11.     {
  12.         var result = ApplyFilters(image);  // CPU 密集
  13.         processed.Add(result);
  14.     });
  15.    
  16.     // 步骤 3:I/O 密集 - 并发保存(异步)
  17.     var saveTasks = processed.Select(img => SaveImageAsync(img));
  18.     await Task.WhenAll(saveTasks);
  19. }
复制代码
关键洞察

  • I/O 步调用 async/await(期待期间不占用线程
  • CPU 步调用 Parallel(充实利用多核)
  • 不要肴杂:I/O 不必要 Task.Run
💡 实战案例:从 100ms 到 10ms 的优化之旅

让我分享一个真实的优化案例,展示怎样辨认和办理性能标题。
案例配景

一个电商 API,用户查询订单详情:

  • 查询数据库(50ms)
  • 调用物流 API(100ms)
  • 调用付出 API(80ms)
初始性能:总耗时 230ms
第一版:同步壅闭(新手代码)
  1. // ❌ 性能:230ms,吞吐量:100 QPS
  2. public IActionResult GetOrder(int orderId)
  3. {
  4.     // 阻塞等待数据库
  5.     var order = _db.Orders.FirstOrDefault(o => o.Id == orderId);
  6.    
  7.     // 阻塞等待物流 API
  8.     var logistics = _logisticsClient.GetAsync(order.TrackingNo).Result;
  9.    
  10.     // 阻塞等待支付 API
  11.     var payment = _paymentClient.GetAsync(order.PaymentId).Result;
  12.    
  13.     return Ok(new { order, logistics, payment });
  14. }
复制代码
标题:3 个线程在干什么?傻等 I/O
第二版:异步串行(低级优化)
  1. // ⚠️  性能:230ms,吞吐量:10000 QPS
  2. public async Task<IActionResult> GetOrderAsync(int orderId)
  3. {
  4.     // 异步查询数据库
  5.     var order = await _db.Orders.FirstOrDefaultAsync(o => o.Id == orderId);
  6.    
  7.     // 异步调用物流 API
  8.     var logistics = await _logisticsClient.GetAsync(order.TrackingNo);
  9.    
  10.     // 异步调用支付 API
  11.     var payment = await _paymentClient.GetAsync(order.PaymentId);
  12.    
  13.     return Ok(new { order, logistics, payment });
  14. }
复制代码
改进

  • ✅ 吞吐量提拔 100 倍(线程不再壅闭)
  • ❌ 但相应时间没变(仍然是串行)
第三版:异步并发(高级优化)
  1. // ✅ 性能:100ms,吞吐量:10000 QPS
  2. public async Task<IActionResult> GetOrderAsync(int orderId)
  3. {
  4.     // 异步查询数据库
  5.     var order = await _db.Orders.FirstOrDefaultAsync(o => o.Id == orderId);
  6.    
  7.     // 并发调用两个 API(它们互不依赖)
  8.     var logisticsTask = _logisticsClient.GetAsync(order.TrackingNo);
  9.     var paymentTask = _paymentClient.GetAsync(order.PaymentId);
  10.    
  11.     // 等待两个任务都完成
  12.     await Task.WhenAll(logisticsTask, paymentTask);
  13.    
  14.     return Ok(new
  15.     {
  16.         order,
  17.         logistics = logisticsTask.Result,
  18.         payment = paymentTask.Result
  19.     });
  20. }
复制代码
最闭幕果

  • ✅ 相应时间:230ms → 100ms(提拔 2.3 倍)
  • ✅ 吞吐量:100 QPS → 10000 QPS(提拔 100 倍)
关键洞察

  • 物流和付出 API 可以并发调用(它们互不依靠)
  • 100ms 是最慢的谁人 API 的耗时
⚠️ 常见误区:为什么 90% 的开辟者会犯这些错?

误区 1:"async 就是多线程"

错误认知:加了 async 关键字就会创建新线程。
原形:async 只是编译器的语法糖,天生一个状态机。
证实
  1. public async Task TestAsync()
  2. {
  3.     Console.WriteLine($"Before: {Thread.CurrentThread.ManagedThreadId}");
  4.     await Task.Delay(1000);  // 异步等待
  5.     Console.WriteLine($"After: {Thread.CurrentThread.ManagedThreadId}");
  6. }
复制代码
运行效果:线程 ID 大概类似
为什么会有这个误区?

  • 由于 Task.Run 确实会用线程池线程
  • 但 await 本身不会创建线程
误区 2:"Task.Run 能提拔性能"

错误认知:把任何操纵包装在 Task.Run 里就能变快。
原形(来自 CommonMistakesDemo.cs):
  1. // ❌ 错误:浪费线程
  2. var data = await Task.Run(async () =>
  3. {
  4.     await Task.Delay(500);  // I/O 操作
  5.     return "Data";
  6. });
  7. // ✅ 正确:直接 await
  8. await Task.Delay(500);
  9. var data = "Data";
复制代码
为什么错?

  • Task.Delay 已经是异步的(不占用线程)
  • Task.Run 额外占用一个线程池线程
  • 这个线程在 await Task.Delay 期间照旧被开释了
  • 效果:多余的线程调治开销,性能反而低落
准确利用 Task.Run:仅用于 CPU 麋集型使命!
误区 3:"Task 就是线程"

错误认知:创建 1000 个 Task 就会创建 1000 个线程。
原形

  • I/O 麋集型 Task:在期待期间不占用线程(利用 I/O 完成端口)
  • CPU 麋集型 Task:利用线程池线程(通常 < 100 个)
验证代码
  1. // 创建 10000 个 I/O 密集型 Task
  2. var tasks = Enumerable.Range(1, 10000)
  3.     .Select(_ => Task.Delay(5000))
  4.     .ToArray();
  5. await Task.WhenAll(tasks);
  6. // 线程池线程数:几乎没增加!
复制代码
为什么会有这个误区?

  • 由于在其他语言(如 Go、Erlang)中,一个使命确实对应一个"协程"
  • 但 .NET 的 Task 是异步操纵的抽象,不便是线程
🎯 速查表:30 秒做出准确选择

场景特性保举技能制止性能提拔Web API 调用期待网络相应async/await + HttpClientTask.Run10-100x数据库查询期待数据库相应async/await + EF Core Async.Result / .Wait()10-100x文件读写期待磁盘 I/Oasync/await + Stream同步 I/O5-20x图像处理惩罚CPU 盘算Parallel.ForEachasync/await2.5-3.5x视频编码CPU 盘算Parallel + Task.Run单线程2.5-3.5x数据分析CPU 盘算PLINQ平凡 LINQ2.5-3.5x批量下载I/O 麋集Task.WhenAll + async/await串行下载文件数倍混淆使命I/O + CPU组合利用全用异步或全用并行5-20x记着一个原则

  • 等 I/O → async/await
  • 等 CPU → Parallel/PLINQ
📌 本章小结:从狐疑到清晰

核心洞察


  • 并发、并行、异步的本质

    • 并发 = 构造代码的方式(逻辑层面)
    • 并行 = 利用多核硬件(物理层面)
    • 异步 = 不浪费期待时间(实行模式)

  • 技能选择的黄金法则

    • I/O 麋集 → async/await(开释线程)
    • CPU 麋集 → Parallel(利用多核)
    • 混淆型 → 组合利用

  • 常见误区的根源

    • Task ≠ 线程
    • async ≠ 多线程
    • Task.Run ≠ 性能提拔

  • 性能优化的原形

    • 不是"让代码跑得快"
    • 而是"不浪费资源"

💭 思考题

标题 1:为什么异步能提拔吞吐量,但不肯定能低落相应时间?

💡 答案分析
吞吐量 vs 相应时间

  • 吞吐量(Throughput)= 单元时间处理惩罚的哀求数
  • 相应时间(Latency)= 单个哀求的完成时间
异步的核心是开释线程,让一个线程能处理惩罚更多哀求:

  • 同步:100 个线程 → 处理惩罚 100 个哀求
  • 异步:100 个线程 → 处理惩罚 10000 个哀求(线程被复用)
但单个哀求的耗时(如网络延长 100ms)不会变。
要低落相应时间,必要

  • 并发实行(Task.WhenAll)
  • 缓存
  • 更快的网络/数据库
标题 2:下面的代码有什么标题?
  1. public async Task ProcessDataAsync()
  2. {
  3.     var data = await DownloadDataAsync();  // I/O
  4.     var result = await Task.Run(() =>
  5.     {
  6.         return data.Select(x => x * 2).ToList();  // CPU?
  7.     });
  8. }
复制代码
💡 答案分析
标题:Select 操纵不是 CPU 麋集型,不必要 Task.Run。
改进
  1. public async Task ProcessDataAsync()
  2. {
  3.     var data = await DownloadDataAsync();  // I/O
  4.     var result = data.Select(x => x * 2).ToList();  // 同步即可
  5. }
复制代码
什么时间必要 Task.Run?

  • 循环次数 > 10000
  • 单次循环耗时 > 1ms
  • 总耗时 > 100ms
简朴的 LINQ 操纵不必要!
标题 3:为什么 ASP.NET Core 猛烈发起全部利用异步?

💡 答案分析
Web 应用的特点

  • 99% 的时间在等 I/O(数据库、缓存、外部 API)
  • 哀求数量大(大概同时有数千个哀求)
同步的标题

  • 100 个线程 → 只能处理惩罚 100 个并发哀求
  • 第 101 个哀求被拒绝(HTTP 503)
异步的上风

  • 100 个线程 → 可以处理惩罚 10000+ 个并发哀求
  • 吞吐量提拔 100 倍
现实数据(微软官方测试):

  • 同步:1000 并发 → CPU 100%,相应时间 5s
  • 异步:1000 并发 → CPU 20%,相应时间 100ms
🚀 下一步

在下一章《02. 线程的底层:Thread、ThreadPool 与 Task 的关系》中,我们将:

  • 用代码实行验证 Thread 的真实本钱(内存、上下文切换)
  • 观察 ThreadPool 的工作偷取算法(为什么比你手动管理更高效)
  • 明白 Task 如安在底层调治(状态机、线程复用)
  • 回复为什么当代 .NET 保举 Task 而不是 Thread
预报一个震撼的实行
  1. // 创建 10000 个 Thread:内存占用 10GB+,系统崩溃
  2. // 创建 10000 个 Task:内存占用 < 100MB,完美运行
复制代码
示例代码堆栈:https://github.com/Naughtyhusky/csharp-concurrency-cookbook
有标题?接待留言讨论!

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表