Как я могу заставить HttpWebRequest вести себя максимально синхронно? - PullRequest
6 голосов
/ 04 сентября 2011

Резюме: это вообще возможно?

unit test function on ui thread:
- creates a background thread
- starts the thread
- waits for it to complete (function does not exit until it completes!)

background thread:
- opens an http stream 
- reads a url from the web
- terminates

Мое подозрение: фреймворк асинхронно помещает результат в некоторую внутреннюю очередь сообщений, и поэтому ответный обратный вызов никогда не будет вызываться, пока стек потока пользовательского интерфейса не раскрутится и не перейдет к какой-либо функции потока пользовательского интерфейса для накачки стека.


Полная история:

Я портирую приложение, которое требует создания потока из различных источников, один из которых из простого http URL. Я делаю это в фоновом потоке, и в идеале я хотел бы, чтобы он вел себя на 100% синхронно, просто блокируйте при необходимости (это нормально, так как это в фоновом потоке).

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

Я наткнулся на следующую статью: http://pieterderycke.wordpress.com/2011/05/23/adding-synchronous-methods-to-webrequest-on-windows-phone-7/, который предлагает решение, чтобы сделать http веб-запрос синхронным. Но, как это реализовано, я получаю исключение ProtocolViolationException. С тех пор я сделал изменение в коде, чтобы использовать BeginGetResponse () вместо BeginGetRequestStream (), и это, похоже, больше не вызывает исключение.

Но похоже, что фоновый поток теперь блокируется на неопределенный срок. В моем потоке пользовательского интерфейса я зацикливаюсь, выполняя Thread.Sleep (10), поскольку я нахожусь в функции модульного теста, ожидая, когда будет вызван мой обратный вызов. Возможно ли, что обратный вызов не будет вызываться до тех пор, пока не вернется функция модульного теста и поток пользовательского интерфейса не сможет перекачать сообщения? Если это так, каким-либо образом я могу заставить его качать, чтобы я мог продолжить с того места, на котором остановился в процедуре модульного тестирования?

В нижней части упомянутой выше статьи делается комментарий: «Если вы протестируете свой код, вы обнаружите, что он блокируется, если вы выполняете его в потоке пользовательского интерфейса». но я выполняю его в фоновом потоке, так что должно быть в порядке, верно?

В документах msdn показано только, как выполнять асинхронные вызовы. И они также отмечают, что «метод BeginGetResponse требует выполнения некоторых задач синхронной настройки для« ... »обычно нескольких секунд» ... но «может занять 60 секунд или более». Это звучит довольно плохо, чтобы быть выполненным в пользовательском потоке. http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse.aspx

Пожалуйста, помогите!

Вот мой код:

using System.Net;
using System.Threading;
using System;
using System.IO;

namespace Blah
{
  // http://pieterderycke.wordpress.com/2011/05/23/adding-synchronous-methods-to-webrequest-on-windows-phone-7/
  // Creates synchronous web requests.
  // Must not be called on UI threads.

  public static class WebRequestExtensions
  {
    public static Stream GetRequestStream(this WebRequest request)
    {
      AutoResetEvent autoResetEvent = new AutoResetEvent(false);

      IAsyncResult asyncResult = null;
      {
        // /178566/kak-ispolzovat-httpwebrequest-s-metodom-get

        if (request.Method == "GET")
        {
          asyncResult = request.BeginGetResponse(
           r => autoResetEvent.Set(), null);
        }
        else
        {
          asyncResult = request.BeginGetRequestStream(
           r => autoResetEvent.Set(), null);
        }
      }

      // Wait until the call is finished
      autoResetEvent.WaitOne();

      return request.EndGetRequestStream(asyncResult);
    }
  }
}

Я также недавно наткнулся на http://www.eggheadcafe.com/tutorials/aspnet/91f69224-3da5-4959-9901-c5c717c9b184/making-silverlight-emulate-synchronous-requests.aspx,, но это та же проблема. Кажется, я не получаю свой обратный вызов, пока поток пользовательского интерфейса не вернется в стек ... Я чувствую запах какой-то очереди сообщений фреймворка где-то там, я прав?

Спасибо

Ответы [ 4 ]

3 голосов
/ 04 сентября 2011

Поскольку при обработке ответа HTTP используется поток пользовательского интерфейса, его блокировка не позволит завершить запрос.

Если вы используете биты Silverlight Unit Testing Framework, вы можете пометить свой тест как [Asynchronous]:

using System;
using System.Net;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Silverlight.Testing;

[TestClass]
public class WebRequestsTests : WorkItemTest
{
    [TestMethod, Asynchronous]
    public void TestWebRequest()
    {
        var webRequest = WebRequest.CreateHttp("http://www.stackoverflow.com");

        webRequest.BeginGetResponse(result =>
        {
            EnqueueCallback(() =>
            {
                WebResponse response = webRequest.EndGetResponse(result);

                // process response 

                TestComplete(); // async test complete 
            });
        }, null);
    }
} 

В качестве альтернативы, если вы хотите использовать Rx (что я и делаю лично), я недавно написал в блоге о том, как сделать асинхронные тесты на основе Rx более чистыми при использовании инфраструктуры тестирования SL.

2 голосов
/ 05 сентября 2011

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

1 голос
/ 04 сентября 2011

Вы также можете использовать Rx (поставляется с библиотекой), чтобы сделать это.

http://dotnet.dzone.com/articles/5-minute-sample-fromasyncpattern

1 голос
/ 04 сентября 2011

Зачем просить плохое решение не той проблемы?Если вам нужны продолжения для асинхронных запросов, вам просто нужно использовать библиотеку параллельных задач.

Вы можете найти версию TPL для Windows Phone на NuGet: Библиотека параллельных задач для Silverlight .

Это позволяет продолжения, которые по сути дают тот же эффект, что и блокировка вашего потока.

(Роберт МакЛоуз, создатель библиотеки) NuGet - это система, которая позволяет легко управлять зависимостями.Просто перейдите по ссылке http://nuget.org/,, нажмите на ссылку, чтобы установить ее, затем откройте консоль диспетчера пакетов и введите «Install-Package System.Threading.Tasks», и она автоматически установит нужную версию для вашего проекта.Я в настоящее время не делаю источник доступным, хотя я мог бы когда-нибудь в будущем.Мне нужно проверить с парнями из Mono, так как код в основном приходит из Mono.

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