Проблема производительности потоков .NET TPL - PullRequest
0 голосов
/ 20 мая 2018

Я работаю над проектом утилизации и использую .NET Core 2.0.Мой клиент предоставил около 1 миллиона доменов, и клиент попросил меня проверить, активен ли домен, и убедиться, что ответ в порядке.Мой код ниже.Когда я читаю домен из файла, у меня нет никаких проблем, и производительность отличная.

Моя проблема: когда я использую 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. RAM: 64 ГБ
  2. Процессор: 8Core
  3. Windows 10

Моя конфигурация сервера Linux ниже

  1. ОЗУ: 4 ГБ
  2. HDD: 80 ГБ SSD
  3. ОС: Ubuntu 16.04
  4. Процессор:4Core

@ 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;
    }

1 Ответ

0 голосов
/ 20 мая 2018

Я разобрался с проблемой и проверил то же самое в Linux и работает нормально.Всем спасибо.Вместо семафорного подхода я использовал Parallel.Foreach (), проверил создание потока и проверил результат с помощью ввода.Все выглядит хорошо для завтрашней демонстрации.

...