如果我使用异步,不应该使用更少的线程数?

我的理解是,如果我使用异步,线程会发出Web请求并继续前进。 当响应回来时,另一个线程从那里接收它。 因此,有较少数量的捆绑线程处于空闲状态。 这是不是意味着最大活线程数会下降? 但在下面的示例中,不使用异步的代码最终使用较少数量的线程。 有人可以解释为什么吗?

没有异步的代码(使用较小的线程):

using System; using System.Diagnostics; using System.IO; using System.Net; using System.Threading; namespace NoAsync { internal class Program { private const int totalCalls = 100; private static void Main(string[] args) { for (int i = 1; i <= totalCalls; i++) { ThreadPool.QueueUserWorkItem(GoogleSearch, i); } Thread.Sleep(100000); } private static void GoogleSearch(object searchTerm) { Thread.CurrentThread.IsBackground = false; string url = @"https://www.google.com/search?q=" + searchTerm; Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); WebRequest wr = WebRequest.Create(url); var httpWebResponse = (HttpWebResponse) wr.GetResponse(); var reader = new StreamReader(httpWebResponse.GetResponseStream()); string responseFromServer = reader.ReadToEnd(); //Console.WriteLine(responseFromServer); // Display the content. httpWebResponse.Close(); Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); } } } 

使用异步的代码(使用更multithreading)

 using System; using System.Diagnostics; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; namespace AsyncAwait { internal class Program { private const int totalCalls = 100; private static DateTime start = System.DateTime.Now; private static void Main(string[] args) { var tasks = new List(); for (int i = 1; i <= totalCalls; i++) { var searchTerm = i; var t = GoogleSearch(searchTerm); tasks.Add(t); } Task.WaitAll(tasks.ToArray()); Console.WriteLine("Hit Enter to exit"); Console.ReadLine(); } private static async Task GoogleSearch(object searchTerm) { Thread.CurrentThread.IsBackground = false; string url = @"https://www.google.com/search?q=" + searchTerm; Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); using (var client = new HttpClient()) { using (HttpResponseMessage response = await client.GetAsync(url)) { HttpContent content = response.Content; content.Dispose(); Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); Console.WriteLine("TimeSpan consumed {0}", System.DateTime.Now.Subtract(start)); } } } } 

}

我明白我的结果包括非托管线程。 但是线程总数不应该更低吗?

更新:我使用Noseratio提供的代码更新了异步调用

ThreadPool实际上维护着两个子池,一个用于工作线程,另一个用于IOCP线程。 当GetAsync的结果可用时,从池中分配随机IOCP线程(I / O完成端口)以处理异步HTTP请求的完成。 这就是await执行后的代码。 您可以使用ThreadPool.SetMinThreads/SetMaxThreads控制每个子池的大小,有关此内容的更多信息。

因此,您的非异步代码很难与异步版本进行比较。 为了更公平的比较,您应该坚持使用WebRequest来处理这两种情况,例如:

非异步:

 using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; namespace NoAsync { internal class Program { private const int totalCalls = 100; private static void Main(string[] args) { int maxWorkers, maxIOCPs; ThreadPool.GetMaxThreads(out maxWorkers, out maxIOCPs); int minWorkers, minIOCPs; ThreadPool.GetMinThreads(out minWorkers, out minIOCPs); Console.WriteLine(new { maxWorkers, maxIOCPs, minWorkers, minIOCPs }); ThreadPool.SetMinThreads(100, 100); var tasks = new List(); for (int i = 1; i <= totalCalls; i++) tasks.Add(Task.Run(() => GoogleSearch(i))); Task.WaitAll(tasks.ToArray()); } private static void GoogleSearch(object searchTerm) { string url = @"https://www.google.com/search?q=" + searchTerm; Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); WebRequest wr = WebRequest.Create(url); var httpWebResponse = (HttpWebResponse)wr.GetResponse(); var reader = new StreamReader(httpWebResponse.GetResponseStream()); string responseFromServer = reader.ReadToEnd(); //Console.WriteLine(responseFromServer); // Display the content. reader.Close(); httpWebResponse.Close(); Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); } } } 

异步

 using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; namespace Async { internal class Program { private const int totalCalls = 100; private static void Main(string[] args) { int maxWorkers, maxIOCPs; ThreadPool.GetMaxThreads(out maxWorkers, out maxIOCPs); int minWorkers, minIOCPs; ThreadPool.GetMinThreads(out minWorkers, out minIOCPs); Console.WriteLine(new { maxWorkers, maxIOCPs, minWorkers, minIOCPs }); ThreadPool.SetMinThreads(100, 100); var tasks = new List(); for (int i = 1; i <= totalCalls; i++) tasks.Add(GoogleSearch(i)); Task.WaitAll(tasks.ToArray()); } private static async Task GoogleSearch(object searchTerm) { string url = @"https://www.google.com/search?q=" + searchTerm; Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); WebRequest wr = WebRequest.Create(url); var httpWebResponse = (HttpWebResponse) await wr.GetResponseAsync(); var reader = new StreamReader(httpWebResponse.GetResponseStream()); string responseFromServer = await reader.ReadToEndAsync(); //Console.WriteLine(responseFromServer); // Display the content. reader.Close(); httpWebResponse.Close(); Console.WriteLine("Total number of threads in use={0}", Process.GetCurrentProcess().Threads.Count); } } } 

默认情况下,我在系统上看到了以下关于线程池的数字:

 {maxWorkers = 32767,maxIOCPs = 1000,minWorkers = 4,minIOCPs = 4}

增长线程时线程池是懒惰的 。 新的线程创建可以延迟最多500ms(有关更多详细信息,请查看Joe Duffy的“CLR线程池注入,口吃问题” )。

若要解决此问题,请使用ThreadPool.SetMinThreads 。 使用SetMinThreads(100, 100) ,我看到同步版本的峰值为~111个线程,而异步版本的峰值为~20个线程 (发布版本,没有调试器运行)。 代表异步版本,这是一个非常具有指示性的差异。