HttpWebRequest и HttpWebResponse идеальные размеры асинхронного буфера

Я пытаюсь использовать HttpWebRequest и HttpWebResponse в .NET 3.5, выполняя их асинхронно: BeginGetRequestStream, EndGetRequestStream, BeginWrite, EndWrite, BeginGetResponse, EndGetResponse, BeginRead, EndRead - все части обработки запроса асинхронны.

У меня есть несколько потоков, которые отправляют большое количество одновременных запросов. EndRead и EndWrite являются блокирующими операциями - они блокируют текущий поток, пока выполняется фактическое чтение / запись в потоке, я пытаюсь найти идеальный размер буфера ввода / вывода для этих операций.

Мое рассуждение таково: поскольку у меня есть несколько активных запросов одновременно, они будут продолжать запускать обратные вызовы, чтобы поток знал, что какие-то данные доступны или данные были отправлены. Если мои буферы большие, чтение / запись данных по сети займет больше времени, поэтому EndRead / EndWrite будет блокироваться дольше. Это заставит другие запросы в том же потоке ждать немного дольше, поскольку их уведомления должны будут ждать, пока поток не будет разблокирован.

Итак, у меня вопрос: какой размер буфера чтения / записи будет хорошим в этой ситуации? Я думал о 2048 байтах каждый, но некоторые примеры кода, которые я видел в разных блогах, показывают совершенно разные значения.

Заранее спасибо за любые идеи.


person xxbbcc    schedule 05.10.2011    source источник


Ответы (2)


Я думаю, что лучшим решением было бы не слишком беспокоиться о размерах буфера, но не блокировать потоки. Если вы передаете делегат параметру callback методов Begin*, этот обратный вызов выполняется после завершения операции, и вы можете вызвать End* оттуда, что (почти) немедленно вернется. Блокировка не требуется.

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

person svick    schedule 05.10.2011
comment
[Если вы передаете делегат параметру обратного вызова методов Begin *, этот обратный вызов выполняется после завершения операции, и вы можете вызвать End * оттуда, что (почти) немедленно вернется. Блокировка не требуется]. Забавно, кто делает настоящую работу Begin *. Это поток из ThreadPool, который выполняет вызов + ждет вас. чем отличается создание собственного потока, выполняющего ту же работу? - person L.B; 06.10.2011
comment
Это зависит. Но в случае операций ввода-вывода используются порты завершения ввода-вывода. Это означает, что метод Begin начинает работу и настраивает порт. Когда работа завершена, порт запускается, и обратный вызов выполняется в потоке пула потоков. И это отличается от создания для этого нового потока, потому что при создании нового потока всегда выделяется миллион байтов. - person svick; 06.10.2011
comment
Итак, вы говорите: BeginA ждет в делегате fxn, в этом fxn start BeginB ждет в другом делегате и т.д. (вместо вызова A (); B () в потоке). Вы действительно знаете, что его код требует такой сложности. Как я уже сказал в своем ответе, преждевременная оптимизация - это корень всех зол. - person L.B; 06.10.2011
comment
Я ничего не сказал ни о каком ожидании. И это не преждевременная оптимизация, если существует «большое количество одновременных запросов», как сказано в ОП. - person svick; 06.10.2011
comment
Я знаю. Ожидание выполняется потоками, созданными (или уже работающими) с функциями BeginA *, и ваш делегат вызывается, когда готов результат. Это было просто для упрощения рабочего процесса. Я не знаю, насколько это связано с этим вопросом, но мне нравится этот блог (chaosinmotion .com / blog /? p = 622). - person L.B; 06.10.2011
comment
@LB Я не совсем уверен, зачем вы линковали эту страницу chaosinmotion - это выглядит интересно, но я не вижу связи. Использование большого количества потоков для решения проблемы, связанной с вводом-выводом, является плохим решением: оно использует много системных ресурсов, в то время как большинство потоков не работают. В этом случае я использую HttpWebRequest, который использует ThreadPool и его потоки порта завершения; там происходит ожидание. Мой код просто выполняет чтение / запись потока, которые блокируют операции. Чем быстрее они завершатся, тем быстрее поток сможет получить новые обратные вызовы от других потоков порта завершения. - person xxbbcc; 06.10.2011
comment
@svick: спасибо, я думаю, вы правы - я забыл, что обратный вызов будет выполняться в потоке порта завершения. Вы правы, мне, наверное, не нужно так сильно беспокоиться о размерах буфера. - person xxbbcc; 06.10.2011

Нет окончательного правила относительно фактических значений, которые вы должны установить, кроме как избегать очевидных крайностей. Это действительно зависит от типа передаваемых данных и их количества. Вероятно, вы захотите установить достаточно высокий буфер записи, но оставьте буфер чтения ниже. Это связано с тем, что запись (обычно) дороже, чем чтение, когда дело доходит до такого рода вещей.

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

person Polynomial    schedule 05.10.2011