GSe*_*erg 2 c# httpwebrequest async-await .net-4.5
我有一堆很慢的函数,基本上是这样的:
private async Task<List<string>> DownloadSomething()
{
var request = System.Net.WebRequest.Create("https://valid.url");
...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
Run Code Online (Sandbox Code Playgroud)
除了调用之外,这很好地和异步地工作WebRequest.Create- 这一行将UI线程冻结几秒钟,这会破坏async/await的目的.
我已经使用BackgroundWorkers 编写了这个代码,它完美运行并且永远不会冻结UI.
但是,创建与async/await相关的Web请求的正确,惯用方法是什么?或者也许应该使用另一个类?
我已经看到了关于异步a的好答案WebRequest,但即使在那里,对象本身也是同步创建的.
有趣的是,我没有看到阻止延迟WebRequest.Create或HttpClient.PostAsync.它可能与DNS解析或代理配置有关,尽管我也希望这些操作在内部实现为异步.
无论如何,作为一种解决方法,您可以在池线程上启动请求,虽然这不是我通常做的事情:
private async Task<List<string>> DownloadSomething()
{
var request = await Task.Run(() => {
// WebRequest.Create freezes??
return System.Net.WebRequest.Create("https://valid.url");
});
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
Run Code Online (Sandbox Code Playgroud)
这将使UI保持响应,但如果用户想要停止操作,则可能很难取消它.那是因为你需要有一个WebRequest实例才能调用Abort它.
使用HttpClient,取消是可能的,如下所示:
private async Task<List<string>> DownloadSomething(CancellationToken token)
{
var httpClient = new HttpClient();
var response = await Task.Run(async () => {
return await httpClient.PostAsync("https://valid.url", token);
}, token);
// ...
}
Run Code Online (Sandbox Code Playgroud)
使用HttpClient,您还可以httpClient.CancelPendingRequests()在取消令牌上注册回调,如下所示.
Task.Run)您可能不需要该IProgress<I>模式.只要DownloadSomething()在UI线程上调用,每个await内部之后的每个执行步骤都DownloadSomething将在同一个UI线程上恢复,因此您可以直接在两者之间更新UI awaits.
现在,要在池线程上运行整个DownloadSomething()via Task.Run,您必须将一个实例传递IProgress<I>给它,例如:
private async Task<List<string>> DownloadSomething(
string url,
IProgress<int> progress,
CancellationToken token)
{
var request = System.Net.WebRequest.Create(url);
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
// read stream and return data
progress.Report(...); // report progress
}
}
// ...
// Calling DownloadSomething from the UI thread via Task.Run:
var progressIndicator = new Progress<int>(ReportProgress);
var cts = new CancellationTokenSource(30000); // cancel in 30s (optional)
var url = "https://valid.url";
var result = await Task.Run(() =>
DownloadSomething(url, progressIndicator, cts.Token), cts.Token);
// the "result" type is deduced to "List<string>" by the compiler
Run Code Online (Sandbox Code Playgroud)
注意,因为它本身DownloadSomething就是一个async方法,所以它现在作为嵌套任务运行,它可以Task.Run透明地为你解开.更多相关信息:Task.Run vs Task.Factory.StartNew.
另请参阅:在异步API中启用进度和取消.
| 归档时间: |
|
| 查看次数: |
6635 次 |
| 最近记录: |