一个简单的 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 秒之后才会创建新的线程。