我正在尝试异步获取HttpWebResponse
对象,以便我可以取消请求(使用BackgroundWorker.CancellationPending
标志)。但是,下面的request.BeginGetResponse
行会阻止,直到返回响应(这可能需要超过一分钟)。
Public Function myGetResponse(ByVal request As HttpWebRequest, ByRef caller As System.ComponentModel.BackgroundWorker) As HttpWebResponse
'set a flag so that we can wait until our async getresponse finishes
Dim waitFlag = New ManualResetEvent(False)
Dim recieveException As Exception = Nothing
Dim response As HttpWebResponse = Nothing
'I use beginGetResponse here and then wait for the result, rather than just using getResponse
'so that this can be aborted cleanly.
request.BeginGetResponse(Sub(result As IAsyncResult)
Try 'catch all exceptions here, so they can be raised on the outer thread
response = request.EndGetResponse(result)
Catch e As Exception
recieveException = e
End Try
waitFlag.Set()
End Sub, Nothing)
While Not waitFlag.WaitOne(100) AndAlso Not caller.CancellationPending
'check for cancelation every 100ms
End While
'if our async read from the server returned an exception, raise it here.
If recieveException IsNot Nothing Then
Throw recieveException
End If
Return response
End Function
BeginGetResponse
的{p> The MSDN documentation包含段落
在此方法变为异步之前,BeginGetResponse方法需要完成一些同步设置任务(例如,DNS解析,代理检测和TCP套接字连接)。因此,永远不应在用户界面(UI)线程上调用此方法,因为它可能需要一些时间,通常为几秒钟。在某些未正确配置webproxy脚本的环境中,这可能需要60秒或更长时间。配置文件元素上的downloadTime属性的默认值是一分钟,它占了潜在时间延迟的大部分。
我做错了什么,或者这些初始同步任务是否可能导致延迟?如果是后者,我如何使这个调用实际上异步?
This answer似乎暗示了一个可能的.NET错误,但我知道我使用的URL是有效的(我在本地运行开发服务器)
我正在使用VS 2010和.NET 4.0
答案 0 :(得分:4)
在此之前我遇到的事情可能就是它,虽然这实际上只是在黑暗中刺伤。
您没有显示如何配置HttpWebRequest
对象。如果这是POST请求,则必须使用BeginGetRequestStream
发送数据。正如HttpWebRequest.BeginGetResponse的文档所说:
您的应用程序不能为特定请求混合使用同步和异步方法。如果您调用
BeginGetRequestStream
方法,则必须使用BeginGetResponse
方法检索响应。
答案 1 :(得分:1)
MSDN文档是正确的,如果在UI线程上调用HttpWebRequest.BeginGetResponse
或HttpWebRequest.GetResponse
,确实存在可能阻止UI的同步部分。
因此,您需要在单独的线程上调用它。我建议使用Task Parallel Library。 Task.Factory.FromAsync
可以帮助包裹BeginGetResponse
/ EndGetResponse
,然后您使用Task.Factory.StartNew
,Task.Unwrap
和Task.ContinueWith
来启动池线程上的请求处理完成。这可以进一步增强,以支持取消。
我不熟悉VB.NET来创建这样的示例,但这里是在C#(.NET 4.0,VS2010),未经测试。将此转换为VB.NET不应该是一个问题,API是相同的。
public static class WebResponseExt
{
// convert BeginGetResponse/EndGetResponse into a Task
static public Task<WebResponse> GetResponseTapAsync(
this WebRequest request)
{
return Task.Factory.FromAsync(
(asyncCallback, state) =>
request.BeginGetResponse(asyncCallback, state),
(asyncResult) =>
request.EndGetResponse(asyncResult), null);
}
// call GetResponseTapAsync on a pool thread
// and unwrap the nested task
static public Task<WebResponse> OffloadGetResponseTapAsync(
this WebRequest request, CancellationToken token)
{
return Task.Factory.StartNew(
() => request.GetResponseTapAsync(),
token,
TaskCreationOptions.None,
TaskScheduler.Default).Unwrap();
}
}
class Program
{
void DoRequest(string url, CancellationToken token)
{
var request = HttpWebRequest.Create(url);
var cancellation = token.Register(() => request.Abort());
var task1 = request.OffloadGetResponseTapAsync(token);
var task2 = task1.ContinueWith(
(responseTask) =>
{
cancellation.Dispose();
try
{
WebResponse result = responseTask.Result;
// process result
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
// process
// rethrow if needed
// the exception will be available as task2.Exception
throw;
}
},
token,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
// ...
}
如果您决定继续使用基于Task
的API,请考虑使用task composition patterns like Stephen Toub's Then
。