如何让HttpWebRequest尽可能同步?

时间:2011-09-04 06:31:59

标签: c# silverlight windows-phone-7


总结:这甚至可能吗?

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

我的怀疑:框架异步地将结果放到一些内部消息队列中,因此在ui线程的堆栈展开之前永远不会调用响应回调,并转到某个ui线程函数来抽取堆栈。


全文:

我正在移植一个需要从各种来源创建流的应用程序,其中一个来自简单的http网址。我在后台线程上这样做,理想情况下我希望它100%同步,只需在需要时阻塞(这是好的,因为它在后台线程上)。

但似乎框架有点米老鼠,它假设你将在ui线程上执行请求,因此它将保护编码器不必创建后台线程来执行异步操作。但我可能会遗漏一些东西。

我偶然发现了以下文章: http://pieterderycke.wordpress.com/2011/05/23/adding-synchronous-methods-to-webrequest-on-windows-phone-7/, 这提出了使http web请求同步的解决方案。但是当它实现时,我得到一个ProtocolViolationException。我已经修改了代码以使用BeginGetResponse()而不是BeginGetRequestStream(),这似乎不再导致异常。

但似乎后台线程现在无限期地阻塞。在我的ui线程上,我循环,执行Thread.Sleep(10)因为我在单元测试函数中,等待我的回调被调用。是否有可能在单元测试函数返回并且ui线程有机会抽取消息之前不会调用回调?如果是这样,我可以用任何方式强制它进行泵送,这样我可以继续在单元测试程序中停止的地方吗?

在上面提到的文章的底部,发表评论“如果你测试你的代码,你会发现如果你在UI线程上执行它会死锁。”但是我在后台线程上执行它,所以应该没问题呢?

msdn docs只显示如何进行异步调用。他们还提到“BeginGetResponse方法需要一些同步设置任务才能完成”......“通常需要几秒钟”......但“可能需要60秒或更长时间”。在ui线程上执行这听起来很糟糕。 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;
      {
        // http://stackoverflow.com/questions/253549/how-do-i-use-httpwebrequest-with-get-method

        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,但这也出现了同样的问题。似乎我没有得到我的回调,直到ui线程返回堆栈...我可以闻到某处的某种框架消息队列,我是否正确?

由于

4 个答案:

答案 0 :(得分:3)

由于HTTP响应处理使用UI线程,阻止它将阻止请求完成。

假设您使用的是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(我个人而言),我最近did a blog post了解如何在使用SL测试框架时使基于Rx的异步测试更清晰。

答案 1 :(得分:2)

虽然我主张将内容移动到UI线程,这可能是你最终会遇到的,但有时将同步代码移植到手机上会是很高兴有GetResponse的同步版本。对于我维护的与多个平台兼容的库,I wrote these extensions methods

答案 2 :(得分:1)

为什么要问错误解决问题?如果需要继续异步请求,则只需使用任务并行库。

您可以在NuGet上找到适用于Windows Phone的TPL版本:Task Parallel Library for Silverlight

这允许继续,这基本上会产生与阻止你的线程相同的效果。

(Robert McLaws,图书馆的创建者)NuGet是一个可以轻松管理依赖关系的系统。只需转到http://nuget.org/,单击链接进行安装,然后打开包管理器控制台并键入“Install-Package System.Threading.Tasks”,它将自动为您的项目安装正确的版本。我目前没有提供源代码,但我可能会在将来的某个时间。我需要向Mono人员查询,因为代码主要来自Mono。

答案 3 :(得分:1)

您也可以使用Rx(随库提供)来执行此操作。

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