Почему мое приложение .net не делает более 10 одновременных запросов WebClient?

Я пытаюсь понять тонкости async/await в C# и портов завершения ввода-вывода в Windows, попутно пишу код для проверки своих предположений.

Насколько я понимаю, вызов WebClient.DownloadStringTaskAsync(...) заставит текущий поток зарегистрировать операцию ввода-вывода с портом завершения ввода-вывода (это, вероятно, немного расплывчато, я пока не понимаю деталей), он создаст Task<string> и продолжит выполнение кода. В какой-то момент он встретит await для данной задачи. В этот момент он вернется из текущего метода (ну, он выйдет из какой-то области, я не уверен, может ли эта область быть чем-то другим, кроме метода - мне, вероятно, следует проверить сгенерированный конечный автомат, чтобы понять эту часть) . После завершения операции ввода-вывода поток будет захвачен из пула потоков, ему будет передан результат операции ввода-вывода, и он выполнит остальную часть только что упомянутой области.

Я пытался проверить это поведение, но добрался только до этого кода С#, прежде чем что-то показалось неправильным:

class Program
{
    static void Main(string[] args)
    {
        ThreadPool.SetMaxThreads(30, 30);
        Console.WriteLine("Connection limit is {0}", ServicePointManager.DefaultConnectionLimit);
        for (int i = 0; i < 30; i++)
        {
            FetchAsync(i);
        }

        Console.WriteLine("Done starting requests");
        Console.ReadKey();
    }

    private async static void FetchAsync(int num)
    {
        WebClient wc = new WebClient();
        string result = await wc.DownloadStringTaskAsync("http://localhost/slow/index/15");
        Console.WriteLine("Done #{0}", num);
    }
}

Как вы можете видеть, я использую WebClient для создания 30 запросов к веб-странице (которые, как я знаю, медленные и для ответа потребуется 15 секунд). Запустив этот код, я наблюдаю следующее поведение: через 15 секунд выполняются первые 10 запросов. Еще через 15 секунд выполняются еще 10 запросов, а еще через 15 секунд выполняются оставшиеся запросы. Таким образом, может показаться, что одновременно имеется только 10 невыполненных запросов (используя perfmon, я проверил, что вызываемое веб-приложение имеет 10 текущих запросов). Это почему? Я ожидал 30 одновременных запросов, так как установил максимальное количество потоков для пула потоков в соответствии с приведенным выше кодом.

Этот вопрос SO заставьте меня поверить, что ServicePointManager может иметь какое-то отношение к этому, но поскольку DefaultConnectionLimit всего 2, я полагаю, что связи нет.

Я понимаю, что это довольно длинное описание довольно простого вопроса. Я надеюсь, что кому-то будет легче указать, где именно мои предположения неверны.

Я запускаю это на Windows 7, 64-битной машине.


person Rune    schedule 27.10.2012    source источник
comment
Вы используете это в ОС Windows Server или клиентской ОС (например, Win 7)? Раньше в клиентских версиях Windows были встроенные ограничения на сетевую активность.   -  person    schedule 27.10.2012
comment
Спасибо за комментарий. Я обновил вопрос: я использую Windows 7 на 64-битном Thinkpad. Знаете ли вы, можно ли изменить эти ограничения (или, по крайней мере, проверить)?   -  person Rune    schedule 28.10.2012
comment
Хм. Не могли бы вы проверить журнал Windows на наличие идентификатора события 4228? community.spiceworks.com/windows_event/show/3080-tcpip-4228   -  person    schedule 28.10.2012
comment
Первоначально я имел в виду ограничение, запрещающее людям использовать клиентскую ОС на файловом сервере. Это еще не подтверждено, но, погуглив, я нашел ссылку, которую только что опубликовал. Так что это может быть что-то совсем другое.   -  person    schedule 28.10.2012
comment
В конце концов, может быть что-то ServicePointManager, взгляните на этот вопрос, и он может немного помочь вам в пути. stackoverflow.com/questions/866350/   -  person Karl-Johan Sjögren    schedule 28.10.2012
comment
Спасибо за ваш вклад, ребята. Я проверил предложения: нет событий с идентификатором 4228, изменение ServicePoint.ConnectionLimit не влияет.   -  person Rune    schedule 28.10.2012
comment
@Rune TPL также пытается оптимизировать количество используемых вами потоков. Просто попробуйте использовать чистый класс Thread.   -  person L.B    schedule 28.10.2012
comment
Согласитесь с @LB - это, вероятно, проблема с пулом потоков и не связана с WebClient - используйте потоки напрямую, чтобы исключить это   -  person BrokenGlass    schedule 28.10.2012
comment
Использование простого new Thread(...).Start() показывает такое же поведение.   -  person Rune    schedule 28.10.2012
comment
@Rune, если вы попытались установить ServicePointmanager.DefaultConnectionLimit на разумное число.   -  person L.B    schedule 28.10.2012
comment
Да, я думал, 20 кажется разумным?   -  person Rune    schedule 28.10.2012
comment
В Windows 2008 Server R2 Enterprise я не вижу ограничения в 10 одновременных запросов, поэтому я предполагаю, что это должно зависеть от ОС. Вздох.   -  person Rune    schedule 28.10.2012
comment
Может быть 10 входящих запросов - из Win7 укажите на сервер 2008 R2 в URL   -  person James Manning    schedule 28.10.2012
comment
@JamesManning Хороший вопрос. Я проверю завтра, когда доберусь до офиса (сейчас я не могу проверить из-за проблем с брандмауэром). Спасибо.   -  person Rune    schedule 28.10.2012
comment
Асинхронный ввод-вывод не имеет ничего общего с пулом потоков (за исключением того, что там стоят в очереди обратные вызовы завершения). Смотрите мой ответ.   -  person usr    schedule 28.10.2012


Ответы (1)


Ваш клиент может отправлять произвольное количество одновременных запросов. Однако ваш сервер — localhost. Клиентские ОС Windows имеют ограничение на количество одновременных запросов в веб-приложениях (поэтому приходится покупать серверную лицензию). Этот лимит равен 10. Вы ничего не можете с этим поделать.

Кстати, ваше приложение использует асинхронный ввод-вывод, поэтому вам не нужны никакие потоки (даже неявные потоки пула потоков) во время выполнения ввода-вывода. Это не ваша проблема.

person usr    schedule 27.10.2012
comment
То, что у меня может быть больше невыполненных запросов, чем потоков в пуле, было именно тем, что я пытался продемонстрировать, когда столкнулся с поведением, описанным выше. Во всяком случае, теперь все это имеет смысл. Когда я запускаю его для WS2008, срабатывает ServicePoint.ConnectionLimit. Когда я поднимаю его, я могу получить столько незавершенных запросов, сколько захочу. Спасибо, очень признателен. - person Rune; 28.10.2012