DownloadStringTaskAsync

DownloadStringTaskAsync

由于某些原因,以下程序启动后会暂停。我相信WebClient().DownloadStringTaskAsync()是原因。

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。难道我做错了什么?

我对原始的Netflix下载示例(通过CTP获得)存在相同的问题-按下搜索按钮后,UI会先冻结-一段时间后它会在加载下一部电影时响应。而且我相信在Anders Hejlsberg在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;
}

它可以正常工作。我的意思是它不会加载任何内容,但是在执行工作时会立即启动并且不会阻塞主线程。

编辑

好吧,我现在所相信的是:WebClient.DownloadStringTaskAsync函数被搞砸了。它应该在没有初始阻塞时间的情况下工作,如下所示:
    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循环中恢复执行,然后再从远程服务器返回结果。

请记住,新的异步API仍然是单线程的。因此,WebClient().DownloadStringTaskAsync()仍需要在您的线程上运行,直到准备好请求并将其发送到服务器为止,然后await才可以在Main()中将执行返回给您的程序流。

我认为您看到的结果是由于以下事实造成的:从您的计算机创建和发送请求会花费一些时间。首先,完成后,DownloadStringTaskAsync的实现可以等待网络IO和远程服务器完成,并可以将执行返回给您。

另一方面,您的RunOrdinaryTask方法只是初始化一个任务并为其分配工作量,然后告诉它开始。然后立即返回。这就是使用RunOrdinaryTask时看不到延迟的原因。

以下是有关该主题的一些链接:Eric Lippert's blog(语言设计者之一)以及有关此主题的Jon Skeet's initial blog post。 Eric有5篇有关延续传递样式的文章,这实际上就是asyncawait的意思。如果您想详细了解新功能,则可能需要阅读Eric关于CPS和Async的文章。无论如何,以上两个链接在解释一个非常重要的事实方面都做得很好:

  • 异​​步!=并行

  • 换句话说,asyncawait不会为您增加新线程。它们只是让您在执行阻塞操作时恢复正常流程的执行-在这种情况下,CPU只会在同步程序中处于停滞状态而无所事事,等待某些外部操作完成。

    编辑

    只是要弄清楚发生了什么:DownloadStringTaskAsync设置一个延续,然后在同一线程上调用WebClient.DownloadStringAsync,然后将执行返回给您的代码。因此,在循环开始计数之前看到的阻塞时间是DownloadStringAsync完成所需的时间。带有async和await的程序非常类似于以下程序,该程序与您的程序具有相同的行为:初始块,然后开始计数,中间的某个地方,异步op完成并打印其中的内容请求的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一起玩。

    09-25 15:42