如何创建HttpWebRequest的不中断异步/等待? [英] How to create HttpWebRequest without interrupting async/await?

查看:970
本文介绍了如何创建HttpWebRequest的不中断异步/等待?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个本质上是这一堆慢功能:

I have a bunch of slow functions that are essentially this:

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
    }

}

这很好地工作和异步除了调用 WebRequest.Create - 这一行冻结UI线程几秒钟哪种类型遗址的异步/等待目的

This works nicely and asynchronously except for the call to WebRequest.Create - this single line freezes the UI thread for several seconds which sort of ruins the purpose of async/await.

我已经有使用这样写code 的BackgroundWorker s,这完美的作品,永不死机的用户界面。结果
尽管如此,什么是建立对于Web请求正确,惯用的方式来异步/的await?或也许有,应使用另一个类?

I already have this code written using BackgroundWorkers, which works perfectly and never freezes the UI.
Still, what is the correct, idiomatic way to create a web request with respect to async/await? Or maybe there is another class that should be used?

我见过这个漂亮的答案了解 asyncifying一个的WebRequest ,但即使有对象本身是同步产生。

I've seen this nice answer about asyncifying a WebRequest, but even there the object itself is created synchronously.

推荐答案

有趣的是,我没有看到与 WebRequest.Create 阻塞延误或 HttpClient.PostAsync 。这可能是是与DNS解析或代理配置,虽然我期望这些操作在内部实施异步了。

Interestingly, I'm not seeing a blocking delay with WebRequest.Create or HttpClient.PostAsync. It might be something to do with DNS resolution or proxy configuration, although I'd expect these operations to be implemented internally as asynchronous, too.

不管怎样,作为一个解决方法您可以开始一个线程池的要求,虽然这不是我通常会做的:

Anyway, as a workaround you can start the request on a pool thread, although this is not something I'd normally do:

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
    }
}

这将保持UI响应,但也可能是难以取消,如果用户要停止操作。那是因为你需要已经有一个的WebRequest 实例能够调用中止就可以了。

That would keep the UI responsive, but it might be difficult to cancel it if user wants to stop the operation. That's because you need to already have a WebRequest instance to be able to call Abort on it.

使用的HttpClient ,取消是可能的,是这样的:

Using HttpClient, cancellation would be possible, something like this:

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);

    // ...
}

使用的HttpClient ,您还可以在取消标记注册 httpClient.CancelPendingRequests()回调,如< A HREF =htt​​p://stackoverflow.com/a/20903958/1768303>这个。



[更新] 根据的评论:在你原来的情况下,你可能并不需要 Task.Run 之前) > IProgress&LT; I&GT; 模式。只要 DownloadSomething()被称为UI线程上,在每个等待在<$ C $每次执行一步C> DownloadSomething 将相同的用户界面线程上恢复,所以你可以只直接在有待于。

With HttpClient, you can also register a httpClient.CancelPendingRequests() callback on the cancellation token, like this.


[UPDATE] Based on the comments: in your original case (before introducing Task.Run) you probably did not need the IProgress<I> pattern. As long as DownloadSomething() was called on the UI thread, every execution step after each await inside DownloadSomething would be resumed on the same UI thread, so you could just update the UI directly in between awaits.

现在,全程运行 DownloadSomething()通过 Task.Run 于池中的线程,你将有I&GT;以 IProgress&LT的实例,通过进去,例如:

Now, to run the whole DownloadSomething() via Task.Run on a pool thread, you would have to pass an instance of IProgress<I> into it, e.g.:

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 

请注意,因为 DownloadSomething 异步方法本身,它现在运行一个嵌套的任务,其中 Task.Run 透明解开你。更多内容: Task.Run VS Task.Factory.StartNew

Note, because DownloadSomething is an async method itself, it is now run as a nested task, which Task.Run transparently unwraps for you. More on this: Task.Run vs Task.Factory.StartNew.

另外,请查阅:<一href=\"http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx\"相对=nofollow>启用进步与取消的异步API的。

这篇关于如何创建HttpWebRequest的不中断异步/等待?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆