Как: BeginProcessRequest - PullRequest
       11

Как: BeginProcessRequest

2 голосов
/ 26 января 2010

Написание службы, работающей на IIS.

В основном выглядит так:

void ProcessRequest(HttpContext context)
{
     <Init Stuff>
     <Access DB>   // This may potentially stall for DB access
     <Write Output to conext stream>
}

Задерживая поток в разделе , мы в основном блокируем один из потоков службы IIS. Итак, поиск способа обойти это:

IAsyncResult BeginProcessRequest(HttpContext context,AsyncCallback cb,Object extraData)
{
    this.del = new AsyncTaskDelegate(ProcessRequest);
    this.del.BeginInvoke(context, cb, extraData);  
}
void EndProcessRequest(IAsyncResult result)
{
    this.del.EndInvoke(ar);
}

Похоже, он создает другой поток для вызова ProcessRequest (). Таким образом, мы все еще останавливаемся на , но на этот раз мы останавливаемся, используя поток, который не принадлежит IIS. Для меня это красиво и симметрично, и код остается чистым и легко читаемым.

Разговаривая с коллегой, он говорит, что это ничего не купит, так как нить все еще застопорилась. Я согласен, но возражаю, что это не нить IIS, поэтому она что-то мне покупает. НО он утверждает, что если мы используем BeginProcessRequest (), чтобы убедиться, что только выполняется асинхронно, то мы можем купить намного больше, так как никакие потоки не будут остановлены.

Это псевдокод, поскольку я не проработал детали:

void ProcessRequest(HttpContext context)
{ /* Do Nothing */ }
IAsyncResult BeginProcessRequest(HttpContext context,AsyncCallback cb,Object extraData)
{
     <Init Stuff>
     this.command = <Access DB>.getSQLCommand();
     this.command.BeginExecuteNonQuery(cb,extraData);   // Assume we want to wait for this to complete.
}
void EndProcessRequest(IAsyncResult result)
{
     this.command.EndExecuteNonQuery(result);
     <Write Output to conext stream>
}

Я вижу, что преимущество в том, что это не останавливает поток, выполняющий BeginExecuteNonQuery (). Но для этого требуется, чтобы базовая реализация использовала select (), чтобы определить, когда в вызове БД действительно есть данные, ожидающие чтения. В то время как более простой способ реализовать это, было бы просто остановить поток, ожидающий ответа (в этом случае добавление дополнительной гранулярности ничего не покупает).

Так есть ли у кого-нибудь ссылки, указывающие, что на самом деле лучший метод?
Или есть заметки о том, как работает базовая реализация BeginExecuteNonQuery ()?
Или просто любая общая информация, которая может помочь.

Спасибо

Edit:

Класс SqlConnection содержит пул потоков.

Таким образом, когда вы выполняете BeginExecuteNonQuery () для SqlCommand, ему фактически не нужно создавать поток. Запрос отправляется, и основной поток управления запускает поток из пула, когда данные возвращаются с SQL-сервера. Таким образом, описанный выше вариант 3 не приводит к потере потоков и не приводит к зависанию потока при выполнении асинхронной операции с БД.

1 Ответ

2 голосов
/ 26 января 2010

Проблема здесь в том, что, по логике вещей, вам все еще требуется, чтобы поток был остановлен. Ваш процесс должен принять HttpContext в своем методе ProcessRequest (), сделать что-то (доступ к базе данных) и записать что-то обратно в контекст.

HTTP-природа без сохранения состояния означает, что соединения с сокетами открыты, запрос выполнен, клиент ожидает ответа, а соединение закрыто. У вас нет открытого соединения, просто ожидающего, пока ваш обработчик напишет что-нибудь клиенту. В приведенном выше примере, где EndProcessRequest () получает объект HttpContext для записи выходных данных? Если это тот же, что был передан в BeginRequest, что должно быть, если вы пытаетесь вернуть свои выходные данные этому клиенту, тогда клиент будет тратить все время между ожиданием запроса и ответа - при поддержании открытое соединение - чтобы сервер сделал свое дело и отправил какой-то вывод. Этот контекст и связанное с ним соединение должны оставаться открытыми до тех пор, пока ваш отдельный поток вращается, что означает, что вы фактически блокируете поток ASP.NET в это время.

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

Кроме того, это не нужно. Для того, чтобы время выполнения потока было проблемой, вы должны попытаться обработать больше запросов «одновременно», чем сервер может обработать. Если ваш метод «доступа к базе данных» занимает слишком много времени, вы поставите в очередь свои запросы, что в конечном итоге приведет к тайм-аутам. Решение здесь заключается в том, чтобы максимально сократить время отклика (время отображения страницы и весь доступ к базе данных и т. Д., Требуемый для отображения).

Резюме:

  1. Не запускать ни одного дополнительного потока - это полезно, только если вы используете несколько потоков

  2. Вы должны убедиться, что все дополнительные потоки, которые вы инициируете, будут завершены в течение отведенного времени для HTTP-запроса

  3. Вызывающий поток ASP.NET должен будет ждать, пока ответ не будет записан обратно в контекст Http, или пока не произойдет тайм-аут. Это может привести к появлению потерянных потоков, если вы запускаете новые в методе ProcessRequest.

Короче, не делай этого. Если вам нужно инициировать процессы из BeginRequest, которые являются асинхронными и не должны записывать выходные данные клиенту, рассмотрите возможность создания службы Windows для обработки этих запросов. Запишите каждый запрос в таблицу и проведите опрос этой таблицы, чтобы узнать, что нужно сделать. Попросите вашего клиента опросить эту таблицу, чтобы увидеть, когда результат будет готов (возможно, через AJAX). Это может не подходить для вашего приложения, но является одним из способов решения этой проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...