0

У меня есть приложение, где мне нужно сделать alot httpWebRequest за один раз и получить ответ так быстро, как это возможно для каждого из них. Actualy Мне не нужно быстро получать все ответы, мне нужно получить каждый ответ так быстро, как я могу. Я имею в виду, что должно быть минимальное время между отправкой каждого запроса ang get response.async HttpWebRequest vs sync в многопоточном приложении

Я читал много статей о асинхронном/жду технологий и выбрал это решение:

private static void Main() 
    { 
     ServicePointManager.DefaultConnectionLimit = 100; 
     ServicePointManager.Expect100Continue = false; 
     ServicePointManager.UseNagleAlgorithm = false; 

     ProcessAsync("https://www.google.ru"); 

     Console.ReadKey(); 
    } 

    private static void ProcessAsync(string url) 
    { 
     for (var i = 1; i <= 100; i++) 
      Task.Factory.StartNew(async() => await ProcessInternalAsync(url)); 
    } 

    private static async Task ProcessInternalAsync(string url) 
    { 
     var request = GetRequest(url); 

     var sw = Stopwatch.StartNew(); 
     await GetStringContentAsync(request); 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
    } 

    public static async Task<string> GetStringContentAsync(HttpWebRequest webRequest) 
    { 
     using (var response = (HttpWebResponse) await webRequest.GetResponseAsync() 
                   .ConfigureAwait(false)) 
     { 
      var content = await GetStringContentFromResponseAsync(response) 
       .ConfigureAwait(false); 
      return content; 
     } 
    } 

    private static async Task<string> GetStringContentFromResponseAsync(HttpWebResponse response) 
    { 
     using (var responseStream = GetStreamForResponse(response)) 
     { 
      if (responseStream == null) 
       return null; 
      using (var streamReader = new StreamReader(responseStream)) 
      { 
       var content = await streamReader.ReadToEndAsync() 
               .ConfigureAwait(false); 
       return content; 
      } 
     } 
    }  

И это работает, но Wery медленно .. (500-1600 мса каждый запрос)

Так я пытался " простое "решение без async/wait:

private static void Main() 
    { 
     ServicePointManager.DefaultConnectionLimit = 100; 
     ServicePointManager.Expect100Continue = false; 
     ServicePointManager.UseNagleAlgorithm = false; 

     Process("https://www.google.ru"); 

     Console.ReadKey(); 
    } 

    private static void Process(string url) 
    { 
     for (var i = 1; i <= 100; i++) 
      Task.Factory.StartNew(() => ProcessInternal(url)); 
    } 

    private static void ProcessInternal(string url) 
    { 
     var request = GetRequest(url); 

     var sw = Stopwatch.StartNew(); 
     GetStringContent(request); 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
    } 

    public static string GetStringContent(HttpWebRequest webRequest) 
    { 
     using (var response = (HttpWebResponse) webRequest.GetResponse()) 
     { 
      var content = GetStringContentFromResponse(response); 
      return content; 
     } 
    } 

    private static string GetStringContentFromResponse(HttpWebResponse response) 
    { 
     using (var responseStream = GetStreamForResponse(response)) 
     { 
      if (responseStream == null) 
       return null; 
      using (var streamReader = new StreamReader(responseStream)) 
      { 
       var content = streamReader.ReadToEnd(); 
       return content; 
      } 
     } 
    }  

И он работает намного быстрее! (55-180 мс каждый запрос).

Вот некоторые другие методы:

private static HttpWebRequest GetRequest(string url) 
    { 
     var request = (HttpWebRequest) WebRequest.Create(url); 
     request.Timeout = 15000; 
     request.Proxy = null; 
     request.Headers.Add("Accept-Encoding", "gzip,deflate"); 

     return request; 
    } 

    private static Stream GetStreamForResponse(HttpWebResponse webResponse) 
    { 
     var responseStream = webResponse.GetResponseStream(); 
     if (responseStream == null) 
      return null; 

     Stream stream; 
     switch (webResponse.ContentEncoding.ToUpperInvariant()) 
     { 
      case "GZIP": 
       stream = new GZipStream(responseStream, CompressionMode.Decompress); 
       break; 
      case "DEFLATE": 
       stream = new DeflateStream(responseStream, CompressionMode.Decompress); 
       break; 
      default: 
       stream = responseStream; 
       break; 
     } 
     return stream; 
    } 

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

В решении, где нет асинхронных/ожидающих, у нас есть блок потока при ожидании ответа, и если он не будет Google и будет сервером с длительными операциями, эти запросы заблокируют все мое приложение.

Может быть, есть лучший способ решить мою проблему?

PS: Это "магия": ServicePointManager.Expect100Continue = false; ServicePointManager.UseNagleAlgorithm = false;
это лишь некоторые советы из интернета и я понятия не имею, как она работает или нет ..

Update Спасибо к Servy, я Finaly понимал мою ошибку в измерении. Теперь метод процесса выглядит следующим образом:

private static void Process(string url) 
    { 
     var taskCollection = new List<Task>(); 
     for (var i = 1; i <= 100; i++) 
     { 
      var sw = Stopwatch.StartNew(); 
      taskCollection.Add(Task.Factory.StartNew(() => 
                { 
                 ProcessInternal(url); 
                 sw.Stop(); 
                 Console.WriteLine(sw.ElapsedMilliseconds); 
                })); 
     } 
     Task.WaitAll(taskCollection.ToArray()); 
    } 

Теперь второе решение показывает миллисекунды (300-1000 мс для запроса). Но в любом случае это быстрее, чем async/await. Метод async/await должен быть медленнее?

ответ

1

Используйте секундомер, чтобы измерить время всей операции, и вы обнаружите, что время будет намного ближе друг к другу.

Как правило, вы запускаете секундомер в асинхронной версии раньше, поэтому они появляются намного дольше.

В асинхронном решении вы запускаете каждую отдельную операцию синхронно, а затем асинхронно дожидаетесь завершения их всех. В этом случае секундомеры начинаются практически сразу после запуска приложения.

Для вашего синхронного решения вы даже не запускаете секундомер, пока каждая задача не будет запланирована для запуска в потоке пула потоков, получает выделение для потока и фактически запускается из этого потока. Большая часть ожиданий будет происходить здесь, и вы еще не запустили свой секундомер.

+0

Благодарим вас за ответ. Вы правы, я теряю немного времени там, когда начинаю задачи, но в любом случае разница настолько велика, что проблема не в 5-10 мс. Я переместил секундомер внутри GetStringContent и GetStringContentAsync (это может помочь измерить более справедливое), но нет никакой разницы. –

+0

Как вы думаете, где мне нужно разместить секундомер для измерения времени запроса в async-решении? –

+0

@ ЮрийБржозовский Я заявил, что вы должны делать с самого начала; измерять время всей программы, а не отдельные операции. – Servy

Смежные вопросы