4

Пытаюсь сделать множество асинхронных запросов HttpWebRequest. Подготовил мини тест:

class Program
{
    static void Main(string[] args)
    {
        Test();
        Console.ReadLine();
    }

    public static async void Test()
    {
        for (int i = 0; i < 10; i++)
        {
            int val = i;
            await Task.Run(() => WR(val));
        }
    }

    static async void WR(int msg)
    {
        Console.WriteLine(msg + " begin");

        string url = "https://stackoverflow.com";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "GET";
        var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>
                (request.BeginGetResponse, request.EndGetResponse, null);

        Console.WriteLine(msg + " status code: " + response.StatusCode);
        Console.WriteLine(msg + " end");
    }
}

Но вот что получилось:

0 begin
1 begin
2 begin
3 begin
4 begin
5 begin
6 begin
7 begin
8 begin
9 begin
0 status code: OK
0 end
1 status code: OK
1 end

А после 1 end вообще ничего не происходит. Где-то через 30 секунд в output вылазит:

The thread 0x6634 has exited with code 0 (0x0).
The thread 0x5620 has exited with code 0 (0x0).
The thread 0x4d08 has exited with code 0 (0x0).
The thread 0x39b8 has exited with code 0 (0x0).
The thread 0x3454 has exited with code 0 (0x0).
The thread 0x99c has exited with code 0 (0x0).
The thread 0x6be0 has exited with code 0 (0x0).

Но никаких ошибок в дебаге не вываливается. Подскажите где я ошибся и как исправить?

UPDATE: Затык происходит при запуске в Visual Studio. Интересно что с включенным Fiddler все работает нормально.

  • 1
    Я как то писал асинхронный парсер, думаю, вам поможет – tym32167 Sep 13 '18 at 20:11
  • @tym32167 о блин, я как раз точно такую же задачу решаю))) Спасибо за ссылку, буду вникать) – Denis Ross Sep 13 '18 at 20:16
  • Не совсем. На самом деле разница в том что я использую не HttpClient, а HttpWebRequest. Что интересно, если использовать HttpClient и await client.GetAsync, то подобной проблемы с зависанием и отваливанием не происходит. – Denis Ross Sep 13 '18 at 20:30
  • ну так и используйте HttpClient, у него и асинхронные методы готовы из коробки – tym32167 Sep 13 '18 at 20:33
  • с HttpClient неудобно использовать proxy сервера + мне в будущем понадобятся еще подсовывать куки и постдату - в этом плане HttpWebRequest выглядит привлекательнее. Ну и меня больше волнует сам факт того что мой вариант не работает - хотелось бы разобраться в причинах. – Denis Ross Sep 13 '18 at 20:37
  • Попробуйте фиддлером поглядеть что происходит. Например, stackoverflow может вас по таймауту прокинуть или вернуть ответ, отличный от 200 – tym32167 Sep 13 '18 at 20:38
  • @tym32167 чудеса. С открытым фидлером все запросы прошли норм, но стоило фидлер закрыть и повторить - снова такой косяк лол. Что за фигня Т_Т – Denis Ross Sep 13 '18 at 20:42
  • Раз уж вы используете асинхронные запросы, то Task.Run не нужен. Во всяком случае, неэффективен. – Alexander Petrov Sep 13 '18 at 21:13
  • @AlexanderPetrov ну он позволяет забить сразу все 8 ядер отдельными потоками, почему не вариант то? – Denis Ross Sep 13 '18 at 21:16
  • Это как раз и плохо, что он забивает ядра. Асинхронность вообще не должна потреблять процессор. – Alexander Petrov Sep 13 '18 at 21:31
  • @AlexanderPetrov хм, а чем плохо? просто я реально не силен в этой теме. По логике я выйгрышь в скорости же получаю. В чем минусы? – Denis Ross Sep 13 '18 at 21:36
  • Я недавно наваял ответ на похожую тему. Текста много, но вдруг у вас возникнет желание ознакомиться. – Alexander Petrov Sep 13 '18 at 21:36
  • @AlexanderPetrov в целом более менее понял. Я просто пытался избежать затыка Task.WhenAll в виде ожидания пока все запросы пройдут и видимо перемудрил) Но суть в том что код ни у меня ни у вас не работает, хотя у других все норм. – Denis Ross Sep 13 '18 at 21:47

1 Answers1

3

Переписал немного ваш код. Все прекрасно работает

void  Main()
{
    Test();
    Console.ReadLine();
}

public static async Task Test()
{
    var tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
    {
        int val = i;
        var task =  WR(val);
        tasks.Add(task);
    }
    await Task.WhenAll(tasks);
}

static async Task WR(int msg)
{
    Console.WriteLine(msg + " begin");

    string url = "https://stackoverflow.com";
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = "GET";
    var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>
            (request.BeginGetResponse, request.EndGetResponse, null);

    Console.WriteLine(msg + " status code: " + response.StatusCode);
    Console.WriteLine(msg + " end");

    response.Dispose(); 
}

Вывод

0 begin
1 begin
2 begin
3 begin
4 begin
5 begin
6 begin
7 begin
8 begin
9 begin
3 status code: OK
3 end
5 status code: OK
5 end
0 status code: OK
0 end
6 status code: OK
6 end
2 status code: OK
2 end
7 status code: OK
7 end
1 status code: OK
1 end
8 status code: OK
8 end
9 status code: OK
9 end
4 status code: OK
4 end   

UPD

Не забываем диспозить респонс

    static async Task WR(int msg)
    {
        Console.WriteLine(msg + " begin");

        string url = "https://stackoverflow.com";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "GET";

        using (var response = (HttpWebResponse)
            await Task.Factory
            .FromAsync(request.BeginGetResponse, request.EndGetResponse, null)) //// <<<<<
        {
            Console.WriteLine(msg + " status code: " + response.StatusCode);
            Console.WriteLine(msg + " end");
        }
    }
tym32167
  • 32,857
  • Не, у меня опять 2 запроса и отваливается. Не в коде походу проблема, мне уже на англоязычной версии стака отписались что тоже все работает. Сюрприз в том что при запущенном фидлере у меня этот код тоже выполняется, а при выключенном после 2ух запросов падает. Явно где-то что-то перекрывается, только не представляю что делать. Разве что снести фидлер, но блин - он мне нужен как раз следить за этими запросами... – Denis Ross Sep 13 '18 at 21:39
  • У меня и первоначальный вариант и этот тоже всего два запроса выполняет. Любопытно, в чем дело. – Alexander Petrov Sep 13 '18 at 21:41
  • @AlexanderPetrov а вы чем запускаете код? Я линкупадом – tym32167 Sep 13 '18 at 21:42
  • Я прямо в visual studio. – Denis Ross Sep 13 '18 at 21:45
  • Я в Visual Studio. – Alexander Petrov Sep 13 '18 at 21:45
  • @AlexanderPetrov у меня в студии тоже на втором встало. Интересно – tym32167 Sep 13 '18 at 21:46
  • Вот так дела... Какие есть идеи? – Denis Ross Sep 13 '18 at 21:47
  • @tym32167 а попробуйте с запущенным фиддлером запустить - у меня отрабатывает в таком варианте. хотя это конечно никуда не годится. – Denis Ross Sep 13 '18 at 21:51
  • @DenisRoss см апдейт ответа – tym32167 Sep 13 '18 at 21:52
  • @tym32167 ФАААК!!! УРА, заработало) Я кстати думал в этом направлении и пытался сделать с помощью блока using(...) потому как про Dispose не сообразил, но тут вы ответ прислали) – Denis Ross Sep 13 '18 at 21:55
  • @DenisRoss как раз поправил ответ на using :) – tym32167 Sep 13 '18 at 21:56