.NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况

本文讨论了在.NET中如何不小心地使用Task.Run可能导致线程池资源耗尽,甚至出现类似死锁的情况。通过一个简单的例子,解释了任务耗时增加的原因,并分析了问题的根源在于线程池的工作线程不足。解决方案包括避免不必要的Task转换为同步操作。此外,文章还提到了其他可能导致死锁的问题和相应的解决策略。
摘要由CSDN通过智能技术生成

一个简单的 Task 不会消耗多少时间,但如果你不合适地将 Task 转为同步等待,那么也可能很快耗尽线程池的所有资源,出现类似死锁的情况。

本文将以一个最简单的例子说明如何出现以及避免这样的问题。


耗时的 Task.Run

谁都不会认为 Task.Run(() => 1) 这个异步任务执行会消耗多少时间。

但实际上,如果你的代码写得不清真,它真的能消耗大量的时间,这种时间消耗有点像死锁。

下图分别是 7 个这样的任务、8 个这样的任务和 16 个这样的任务的耗时:

简单异步任务的耗时

可以发现,8 个任务和 16 个任务的耗时很不正常。

在实际的测试当中,1~7 个任务的耗时几乎相同,而到后面每增加一个任务会增加大量时间。

任务个数 耗时 (ms)
1 39
2 54
3 58
4 50
5 49
6 45
7 54
8 1027
9 2030
10 3027
11 4027
12 5032
13 6027
14 7029
15 8025
16 9025

任务计时采用的是 Stopwatch,关于为什么要使用这种计时方式,可以阅读 .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)

统计图表

从图中,我们可以很直观地观察到,每多一个任务,就会多花 1 秒的事件。这可以认为默认情况下线程池在增加线程的时候,发现如果线程不够,会等待 1 秒之后才会创建新的线程。

最简复现代码

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值