异步方法是否意味着多线程
答案是否定的,请看如下源码
class Program
{
static async Task Main(string[] args)
{
decimal maxDec = decimal.MaxValue;
Console.WriteLine($"main thread id:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
decimal sum1 = await GetSumAsync(1000000000L);
Console.WriteLine($"main thread id after async1:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
decimal sum2 = await GetSumAsync(100000000);
Console.WriteLine($"main thread id after async2:{Thread.CurrentThread.ManagedThreadId}");
Console.ReadKey();
}
static async Task<decimal> GetSumAsync(long n)
{
Console.WriteLine($"async method thread id1:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
decimal sum = 0;
Random rand = new Random();
for (int i = 0; i < n; i++)
{
sum += rand.Next(10000);
}
Console.WriteLine($"async method thread id2:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
return sum;
}
执行结果如下图,可以看到线程ID没有发生变化。
为什么.net提供的有些API在调用异步方法之后线程ID变了呢
请看如下代码
static async Task Main(string[] args)
{
decimal maxDec = decimal.MaxValue;
Console.WriteLine($"main thread id:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
string str = new string('a', 10000000);
await File.WriteAllTextAsync(@"C:\test\1.txt", str);
Console.WriteLine($"main thread id after async1:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
await File.WriteAllTextAsync(@"C:\test\1.txt", str);
Console.WriteLine($"main thread id after async2:{Thread.CurrentThread.ManagedThreadId}");
Console.ReadKey();
}
执行完成之后结果如下:
可以看到执行完第一次的 WriteAllTextAsync方法之后线程ID变了,执行完第二次的WriteAllTextAsync之后线程ID没变,这是为什么呢?
通过查看net的runtime相关代码,可以看到有如下一段:
// Rent the reusable ThreadPoolValueTaskSource, or create a new one to use if we couldn't get one (which
// should only happen on first use or if the SafeFileHandle is being used concurrently).
internal ThreadPoolValueTaskSource GetThreadPoolValueTaskSource() =>
Interlocked.Exchange(ref _reusableThreadPoolValueTaskSource, null) ?? new ThreadPoolValueTaskSource(this);
原来是在异步方法里面首先尝试使用原来的线程,没拿到的话会开启一个新的线程。
综上,异步方法本身不会开启新的线程,除非方法内部编写代码开启。