【问题标题】:.NET TPL Thread Performance Issue.NET TPL 线程性能问题
【发布时间】:2018-05-20 11:09:07
【问题描述】:

我正在从事网络抓取项目,我正在使用 .NET Core 2.0。我的客户提供了大约 100 万个域,客户要求我检查域是否处于活动状态并检查响应是否正常。我的代码如下。当我从文件中读取域时,我没有任何问题并且性能非常好。

我的问题是当我使用 PLINQ 并检查域是否处于活动状态时,它只使用一个线程,而对于 25000 个域,大约需要一个小时。当我使用信号量方式时,性能很好,有时结果中的域计数与输入不匹配。例如,在 25000 个域中,我得到的结果类似于 24798,但我不知道剩余的 202 个域在哪里。如何提高性能以及代码中缺少什么?请帮忙。这里我提供的是 PLINQ 和信号量版本的代码。

信号量版本

        var semaphore = new Semaphore(200, 225);
        var allDomains = new List<BsonDocument>();
        try
        {
            foreach (var domain in domainList)
            {
                var cT1 = Task.Factory.StartNew(() =>
                {
                    try
                    {
                        semaphore.WaitOne();
                        Interlocked.Increment(ref countThreads);
                        var active = IsDomainActive(domain) ? true : false;
                        lock (allDomains) allDomains.Add(
                            new BsonDocument
                            {
                                {"Url", domain},                                    
                                {"Active", active},                                    
                                {"CreatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)},
                                {"UpdatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)}
                            }
                        );
                    }
                    finally
                    {
                        semaphore.Release();
                        Interlocked.Decrement(ref countThreads);
                    }
                }, TaskCreationOptions.LongRunning);
            }
        }
        finally
        {
            if (semaphore != null)
            {
                semaphore.Dispose();
                semaphore = null;
            }
        }

PLINQ 版本

var allDomains = (
            from domain in domainList.AsParallel().WithCancellation(cancellationToken).WithDegreeOfParallelism(7).WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            where IsDomainActive(domain)
            select new BsonDocument
            {
                {"Url", domain},                   
                {"CreatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)},
                {"UpdatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)}
            }).ToList();


 private static bool IsDomainActive(string url)
    {
        var domain = new StringBuilder();
        domain.Append("http://");
        domain.Append(url);
        Console.WriteLine($"IsDomainActive: {url:00} - On Thread " + $"{Thread.CurrentThread.ManagedThreadId:00}. Concurrent: {countThreads}");
        try
        {                               
            var request = (HttpWebRequest) WebRequest.Create(new Uri(domain.ToString()));             
            request.Timeout = 5000;
            request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2";
            var response = (HttpWebResponse)request.GetResponse();
            return (response == null || response.StatusCode != HttpStatusCode.OK) ? false : true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

我的电脑配置如下,问题在两个环境中都出现。

  1. 内存:64GB
  2. 处理器:8 核
  3. Windows 10

我的 Linux 服务器配置如下

  1. 内存:4GB
  2. 硬盘:80GB 固态硬盘
  3. 操作系统:Ubuntu 16.04
  4. 处理器:4 核

@edit1

我已经用 HttpClient.GetAsync 更新了代码,但性能仍然很慢,甚至 1000 个域也需要很多时间。

private static async Task<bool> IsDomainActive(string url)
    {
        var domain = new StringBuilder();
        domain.Append("http://");
        domain.Append(url);
        Console.WriteLine("Processing Domain: " + url);
        try
        {
            var sessionId = (new Random()).Next().ToString();
            var netProxy = new WebProxy("<proxyserver>", port);
            login = "<login>";
            password = "<password>";
            netProxy.Credentials = new NetworkCredential(login, password);
            var handler = new HttpClientHandler()
            {
                Proxy = netProxy,
                UseProxy = true,
            };
            var httpClient = new HttpClient(handler);
            var request = new HttpRequestMessage() {
                RequestUri = new Uri(domain.ToString()),
                Method = HttpMethod.Get
            };
            request.Headers.Add("Timeout","5000");
            request.Headers.Add("UserAgent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2");
            var response = await httpClient.SendAsync(request).ConfigureAwait(false);
            response.EnsureSuccessStatusCode();
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

@edit2 将列表更改为并发。

private static List<BsonDocument> ProcessFile(ConcurrentBag<string> domains, IProgress<string> progress,
        CancellationToken cancellationToken)
    {
       
        var allDomains = (from domain in domains.AsParallel().WithCancellation(cancellationToken)
                .WithDegreeOfParallelism(Environment.ProcessorCount)
                .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            where IsDomainActive(domain).Result
            select new BsonDocument
            {
                        {"Url", domain},                           
                        {"Protocol", "http"},
                        {"CreatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)},
                        {"UpdatedOn", DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)}
            }).ToList();
        return allDomains;
    }

【问题讨论】:

  • 为什么并行度设置为 7?为什么不使用像 HttpClient.GetAsync() 这样的原生异步调用?
  • 我设置 DegreeOfParallelism = 7 的原因。我的电脑配置是8核处理器。测试完成后,我计划更改为 Environment.ProcessorCount。因为我的电脑和服务器配置不同。在哪里使用 HttpClient.GetAsync()?请告诉我。
  • 任何帮助将不胜感激。
  • 您使用的不是线程安全的集合,您不能在并行代码中使用 List。重用 HtpClient 实例。您无需在第一个版本的代码中等待您的任务,这就是为什么它更快。
  • 谢谢。我已经用我的问题更新了代码。因为在评论中,它不允许我添加代码。 :-( 不知道为什么。

标签: .net multithreading performance task-parallel-library semaphore


【解决方案1】:

我已经解决了这个问题并在 linux 中进行了测试,并且工作正常。谢谢大家。而不是信号量方法,我使用了 Parallel.Foreach() 并检查了线程创建并使用输入验证了结果。明天演示的一切看起来都很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    相关资源
    最近更新 更多