我正在尝试使 HttpWebResponse
对象异,以便我可以取消请求(使用 BackgroundWorker.CancellationPending
flag)。但是,下方的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
的MSDN文档包括段落,
BegingEtresponse方法需要一些同步设置任务才能完成(例如,DNS分辨率,代理检测和TCP套接字连接),然后该方法变得异步。结果,该方法绝不能在用户界面(UI)线程上调用,因为通常需要一些时间,通常需要几秒钟。在某些WebProxy脚本未正确配置的环境中,这可能需要60秒或更多。配置文件元素上下载时间属性的默认值是一分钟,该分钟计算大多数潜在的时间延迟。
我是在做错事,还是可能导致延迟的这些初始同步任务?如果是后者,我该如何进行此调用实际异步?
这个答案似乎暗示了一个可能的.NET错误,但是我知道我正在使用的URL有效(我本地运行Dev Server)
我正在使用VS 2010和.NET 4.0
我遇到的东西可能就是它,尽管这实际上只是黑暗中的刺伤。
您不显示如何配置HttpWebRequest
对象。如果这是发布请求,则必须使用BeginGetRequestStream
发送数据。作为httpwebrequest的文档。
您的应用程序无法为特定请求混合同步和异步方法。如果调用
BeginGetRequestStream
方法,则必须使用BeginGetResponse
方法来检索响应。
MSDN文档是正确的,如果在UI线程上调用HttpWebRequest.BeginGetResponse
或HttpWebRequest.GetResponse
,则确实有一个同步部分可以阻止UI。
因此,您需要在单独的线程上调用它。我建议使用任务并行库。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,请考虑使用Stephen Toub的Then
等任务组成模式。