Async HttpWebRequest без ожидания из веб-приложения - PullRequest
21 голосов
/ 01 февраля 2010

В моем веб-приложении (ASP.NET) у меня есть блок кода, который использует HttpWebRequest для вызова службы REST и продолжения выполнения. Прямо сейчас это занимает больше времени, чем я хотел бы выполнить полный веб-запрос. Дело в том, что то, что возвращает сервис REST, бесполезно. В идеале я хотел бы отправить веб-запрос Async в службу REST, а затем НЕ ждать ответа. Проблема в том, что я пробовал использовать

request.BeginGetResponse(New AsyncCallback(AddressOf myFunc), Nothing)

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

Я подозреваю, что ASP.NET может преобразовать его в запрос синхронизации, когда он находится в веб-приложении. Я вынужден поверить в это, потому что есть объект IAsyncResult result, который передается в функцию обратного вызова, и когда я проверяю его свойство CompletedSynchronously, он всегда устанавливается в true.

Кто-нибудь знает, возможно ли выполнить асинхронный HttpWebRequest (без ожидания) из веб-приложения ASP.NET или он всегда преобразуется в синхронный запрос?

Ответы [ 3 ]

28 голосов
/ 01 февраля 2010
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(myUrl);
//set up web request...
ThreadPool.QueueUserWorkItem(o=>{ myRequest.GetResponse(); });

Также известен как Огненный и Забудь.

16 голосов
/ 01 февраля 2010

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

http://weblogs.asp.net/albertpascual/archive/2009/05/14/fire-and-forget-class-for-asp-net.aspx

http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx

http://www.eggheadcafe.com/articles/20060727.asp

надеюсь, это поможет

13 голосов
/ 02 ноября 2012

(Примечание: я начал вводить это как комментарий, но, получив дополнительные знания во время исследования, он стал достаточно большим для ответа)

1. ThreadPool.QueueUserWorkItem

Первые две ссылки, которые предоставляет @ram, а также ответ @Bryan Batchelders, используют противоречивый ThreadPool.QueueUserWorkItem. Это противоречиво в контексте ASP.NET, потому что неосторожное использование может истощить пул потоков , как показывает @Perhentian ссылка .

2. BeginInvoke

Затем я посмотрел на третью ссылку @ram , в которой используется BeginInvoke. По сути, это просто говорит о том, что некоторый код запускается и в другом потоке . Так что здесь нет разрешения.

3. BeginGetResponse

Теперь вернемся к @Perhentians ссылка . В нем говорится, что BeginGetResponse несколько отличается от реального потока, поскольку он использует IOPC Completion Ports (IOPCs) . То, что вы на самом деле ищете, - это решение, которое все еще использует эти IOPC без создания дополнительного потока.

Теперь, чтобы увидеть, как .NET делает это, я попытался покопаться в HttpWebRequest.BeginGetResponse, который действительно является набором внутренних вызовов. Это идет как:

  1. HttpWebRequest.BeginGetResponse
  2. ServicePoint.SubmitRequest
  3. Connection.SubmitRequest
  4. Два варианта:
    • Если соединение установлено: Connection.StartRequest
    • В противном случае: Connection.WaitList.Add (запрос)

A. * 1047 лист ожидания * Сначала рассмотрим вариант 2: указанный WaitList обрабатывается, когда соединение установлено с предыдущим запросом. Взглянув на ConnectStream, участвующий во всей этой цепочке, вы увидите ThreadPool.QueueUserWorkItem с замечанием: // otherwise we queue a work item to parse the chunk // Consider: Will we have an issue of thread dying off // if we make a IO Read from that thread??? Таким образом, по крайней мере, в некоторых сценариях откатов потоки могут все еще непреднамеренно порождаться платформой, просто используя BeginGetResponse! B. Connection.StartRequest

Теперь у нас все еще есть сценарий, где связь ясна. Обратный вызов устанавливается System.Net.Sockets.Socket.BeginConnect и фактически вызывается BeginAccept. Еще несколько копаний показывают вызов ThreadPool.UnsafeRegisterWaitForSingleObject, результат которого используется для ожидания.

Заключение

Наконец, мы можем сказать, что на самом деле происходит при выполнении BeginGetResponse:

// 1. Connecting a socket
UnsafeNclNativeMethods.OSSOCK.WSAConnect(m_handle)

// 2. Registering the callback
m_RegisteredWait = ThreadPool.UnsafeRegisterWaitForSingleObject(m_AsyncEvent, s_RegisteredWaitCallback, this, Timeout.Infinite, true);

// 3. Waiting for the socket to complete
UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(m_handle, m_AsyncEvent.SafeWaitHandle, blockEventBits);

Обратите внимание на Timeout.Infinite . Я все еще выясняю, возможно ли получить сокет, на котором выполняется кодовая дорожка, которая не выполняет ожидание, но сейчас мне это кажется невозможным.

Что касается моего заключения: похоже, не существует простого способа использовать IOCP в сценарии «забей и забудь». Поэтому, если вы не можете заставить указанный сокет не ждать завершения в BeginAccept, вы застряли в ThreadPool.

...