为什么 WebClient.DownloadStringTaskAsync() 会阻塞?- 新的异步 API/语法/CTP [英] Why does WebClient.DownloadStringTaskAsync() block ? - new async API/syntax/CTP

查看:15
本文介绍了为什么 WebClient.DownloadStringTaskAsync() 会阻塞?- 新的异步 API/语法/CTP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于某种原因,下面的程序启动后会出现暂停.我相信 WebClient().DownloadStringTaskAsync() 是原因.

For some reason there is a pause after the program below starts. I believe that WebClient().DownloadStringTaskAsync() is the cause.

class Program
{
    static void Main(string[] args)
    {
        AsyncReturnTask();

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

    public static async void AsyncReturnTask()
    {
        var result = await DownloadAndReturnTaskStringAsync();
        Console.WriteLine(result);
    }

    private static async Task<string> DownloadAndReturnTaskStringAsync()
    {
        return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
    }
}

据我所知,我的程序应该立即从 0 开始计数到 ​​15.我做错了什么吗?

As far as I understand my program should start counting from 0 to 15 immediately. Am I doing something wrong?

原始 Netflix 下载示例(您通过 CTP) - 按下搜索按钮后,用户界面首先冻结 - 一段时间后,它会在加载下一部电影时做出响应.我相信 Anders Hejlsberg 在 PDC 2010 上的演讲并没有冻结.

I had the same problem with the original Netflix download sample (which you get with CTP) - after pressing the search button the UI first freezes - and after some time it is responsive while loadning the next movies. And I believe it didn't freeze in Anders Hejlsberg's presentation at PDC 2010.

还有一件事.当代替

return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));

我使用我自己的方法:

return await ReturnOrdinaryTask();

是:

public static Task<string> ReturnOrdinaryTask()
{
    var t = Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("------------- " + i.ToString());
            Thread.Sleep(100);
        }
        return "some text";
    });
    return t;
}

它应该可以正常工作.我的意思是它不会加载任何东西,但它会立即启动并且在执行其工作时不会阻塞主线程.

It works as it should. I mean it doesn't load anything, but it starts immediately and doesn't block the main thread, while doing its work.

编辑

好的,我现在认为:WebClient.DownloadStringTaskAsync 函数被搞砸了.它应该可以在没有初始阻塞期的情况下工作,如下所示:

OK, what I believe right now is: the WebClient.DownloadStringTaskAsync function is screwed up. It should work without the initial blocking period, like this:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        Task.Factory.StartNew(() =>
            {
                cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
                cli.DownloadStringAsync(new Uri("http://www.weather.gov"));
            });

        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

推荐答案

虽然您的程序确实阻塞了一段时间,但在从远程服务器返回结果之前,它确实会在 for 循环中恢复执行.

While your program does block for a while, it does resume execution in the for loop, before the result is returned from the remote server.

请记住,新的异步 API 仍然是单线程的.所以 WebClient().DownloadStringTaskAsync() 仍然需要在你的线程上运行,直到请求准备好并发送到服务器,然后它才能 await 并返回执行您在 Main() 中的程序流程.

Remember that the new async API is still single-threaded. So WebClient().DownloadStringTaskAsync() still needs to run on your thread until the request has been prepared and sent to the server, before it can await and yield execution back to your program flow in Main().

我认为您看到的结果是因为从您的机器创建和发送请求需要一些时间.首先当它完成后,DownloadStringTaskAsync 的实现可以等待网络 IO 和远程服务器完成,并且可以返回执行给你.

I think the results you are seeing are due to the fact that it takes some time to create and send the request out from your machine. First when that has finished, the implementation of DownloadStringTaskAsync can wait for network IO and the remote server to complete, and can return execution to you.

另一方面,您的 RunOrdinaryTask 方法只是初始化一个任务并为其提供工作负载,然后告诉它开始.然后它立即返回.这就是为什么您在使用 RunOrdinaryTask 时没有看到延迟.

On the other hand, your RunOrdinaryTask method just initializes a task and gives it a workload, and tells it to start. Then it returns immediately. That is why you don't see a delay when using RunOrdinaryTask.

以下是有关该主题的一些链接:Eric Lippert 的博客(语言设计者之一),以及 Jon Skeet 关于它的初始博客文章.Eric 有 5 篇关于延续传递风格的系列博文,这才是 asyncawait 的真正意义所在.如果您想详细了解新功能,您可能需要阅读 Eric 关于 CPS 和 Async 的帖子.无论如何,上面的两个链接都很好地解释了一个非常重要的事实:

Here are some links on the subject: Eric Lippert's blog (one of the language designers), as well as Jon Skeet's initial blog post about it. Eric has a series of 5 posts about continuation-passing style, which really is what async and await is really about. If you want to understand the new feature in detail, you might want to read Eric's posts about CPS and Async. Anyways, both links above does a good job on explaining a very important fact:

  • 异步 != 并行

换句话说,asyncawait 不会为您启动新线程.它们只是让您在执行阻塞操作时恢复正常流程的执行 - 您的 CPU 会坐在同步程序中什么也不做,等待某些外部操作完成.

In other words, async and await does not spin up new threads for you. They just lets you resume execution of your normal flow, when you are doing a blocking operation - times where your CPU would just sit and do nothing in a synchronous program, waiting for some external operation to complete.

只是为了弄清楚发生了什么:DownloadStringTaskAsync 设置一个延续,然后在同一线程上调用 WebClient.DownloadStringAsync,然后em> 将执行返回给您的代码.因此,您在循环开始计数之前看到的阻塞时间是 DownloadStringAsync 完成所需的时间.您使用 async 和 await 的程序非常接近于以下程序,它表现出与您的程序相同的行为:一个初始块,然后开始计数,在中间的某个地方,异步操作完成并打印来自请求的网址:

Just to be clear about what is happening: DownloadStringTaskAsync sets up a continuation, then calls WebClient.DownloadStringAsync, on the same thread, and then yields execution back to your code. Therefore, the blocking time you are seeing before the loop starts counting, is the time it takes DownloadStringAsync to complete. Your program with async and await is very close to be the equivalent of the following program, which exhibits the same behaviour as your program: An initial block, then counting starts, and somewhere in the middle, the async op finishes and prints the content from the requested URL:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
        cli.DownloadStringAsync(new Uri("http://www.weather.gov")); // Blocks until request has been prepared
        
        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

注意:我绝不是这方面的专家,所以我可能在某些方面是错误的.如果您认为这是错误的,请随时纠正我对主题的理解 - 我昨晚刚刚看了 PDC 演示文稿并与 CTP 一起玩.

Note: I am by no means an expert on this subject, so I might be wrong on some points. Feel free to correct my understanding of the subject, if you think this is wrong - I just looked at the PDC presentation and played with the CTP last night.

这篇关于为什么 WebClient.DownloadStringTaskAsync() 会阻塞?- 新的异步 API/语法/CTP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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