Соответствующий параллельный подход для получения номера статуса URL-адресов

#c#

#c#

Вопрос:

Я пытаюсь понять доступные параллельные подходы и пытаюсь узнать, какой из них более подходит для моего текущего сценария. Моя работа заключается в том, чтобы получить статус списка URL-адресов независимо от того, возвращают ли они HttpStatus OK или Not OK одновременно, ограничить количество потоков и убедиться, что предоставленные максимальные потоки выполняются ВСЕ время, независимо от пакетного выполнения потока. Для этого я попытался использовать параллельную библиотеку задач, и она отлично работает.

Код

  var parallelOptions = new ParallelOptions
 {
     MaxDegreeOfParallelism = 3
 };
 Parallel.ForEach(ListMonitorUrl, parallelOptions, i =>
 {
     LogMailError("URL: "   i.URL   " DateTime: "   DateTime.Now   " Thread ID: "   Thread.CurrentThread.ManagedThreadId, LogFile);
     VerifyWebAppUrl(i);
 });

  public MonitorURLs VerifyWebAppUrl(MonitorURLs Model)
  {
      List<string> UrlsWithNotOKResponse = new List<string>();

      try
      {
          var request = (HttpWebRequest)WebRequest.Create(Model.URL);
          using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
          {
              if (response.StatusCode != HttpStatusCode.OK amp;amp; Model.Status == "A")
                  Model.ChangeOfStatus = true;
              if (response.StatusCode == HttpStatusCode.OK amp;amp; Model.Status != "A")
                  Model.ChangeOfStatus = true;
          }
      }
      catch (Exception ex)
      {
          Model.ChangeOfStatus = false;
      }
      return Model;
  }
  

Я видел, что есть и другие подходы, такие как SemaphoreSlim , Task . Но я не уверен, что лучше всего подходит для моего сценария. Пожалуйста, предложите.

Комментарии:

1. «Пожалуйста, предложите» вопросы лучше работать на другой сайт

Ответ №1:

Parallel.ForEach полностью не подходит для рабочих нагрузок, связанных с вводом-выводом, вы расходуете ценные ресурсы, ожидая портов завершения ввода-вывода, и регулируете работу планировщика задач по умолчанию / Threadpool (что не только без необходимости создаст узкое место, но и истощит другие части вашего приложения).

Правильным подходом будет что-то, что поддерживает шаблон Async Await, такой Task.WhenAll как, реактивные расширения, поток данных TPL, и т.д.

Вот простой пример потока данных Microsoft TPL. Вы можете получить систему nuget здесь .Многопоточность.Задачи.Поток данных.

Преимущества заключаются в:

  1. Это работает из коробки с шаблоном Async Await
  2. Вы можете контролировать максимальные степени параллелизма, выбирайте столько, сколько может обработать ваша система / приложение (тогда это значение может быть намного больше Parallel.ForEach )
  3. Он прост в использовании
  4. Вы можете создавать более сложные конвейеры обработки, когда будете готовы
  5. Вы не будете регулировать пул потоков и будете намного эффективнее и производительнее

Пример

 public async List<Model> GetDeadUrls(List<Model> models)
{
   var action = new ActionBlock<Model>(

      // your async method
      model => VerifyWebAppUrl(model),

      //some options
      new ExecutionDataflowBlockOptions()
      {
         // CancellationToken = sometoken,
         // pick as many as your system can handle
         MaxDegreeOfParallelism = 100,

         // doesn't need to be ordered 
         EnsureOrdered = false,

         // slight performance tweak, there is only one
         SingleProducerConstrained = true  
      });
   
   // start
   foreach (var model in models)
      action.Post(model);
   
   // mark it as completed
   action.Complete();

   // wait for it to finish
   await action.Completion;

   // filter the results
   return models.Where(x => x.ChangeOfStatus).ToList();

}
  

Асинхронный метод

 public async Task<Model> VerifyWebAppUrl(Model model)
{
   try
   {
      var request = (HttpWebRequest)WebRequest.Create(model.URL);
      using (var response = (HttpWebResponse) await request.GetResponseAsync())
      {
         if (response.StatusCode != HttpStatusCode.OK amp;amp; model.Status == "A")
            model.ChangeOfStatus = true;
         if (response.StatusCode == HttpStatusCode.OK amp;amp; model.Status != "A")
            model.ChangeOfStatus = true;
      }
   }
   catch (Exception ex)
   {
      // this looks suspicious
      model.ChangeOfStatus = false;
   }
   return model;
}
  

Примечание: Это абсолютно непроверенный и только пример

Комментарии:

1. Теперь я могу понять разницу между операциями ввода-вывода и привязкой к процессору. Как насчет использования Parallel. Foreach с асинхронной операцией?? Не решит ли это проблему потока, ожидающего завершения операции ввода-вывода, поскольку асинхронная обработка может выполняться без ожидания результатов??