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

时间:2014-04-24 04:23:52

标签: c# async-await c#-5.0

我的理解是,如果我使用异步,该线程会发出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);
        }
    }
}

使用异步代码(使用更多线程)

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<Task>();

        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提供的代码更新了异步调用

1 个答案:

答案 0 :(得分:8)

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<Task>();

            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<Task>();

            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 }

线程池在增长线程时 lazy 。新的线程创建可以延迟最多500ms(有关更多详细信息,请查看Joe Duffy的"CLR thread pool injection, stuttering problems")。

要解决此问题,请使用ThreadPool.SetMinThreads。使用SetMinThreads(100, 100) sync 版本的峰值 ~111个线程, async 版本的峰值约为20个线程 (发布版本,无需调试器即可运行)。代表异步版本,这是一个非常具有指示性的差异。

相关问题