15

由于某种原因,下面的程序启动后会出现暂停。我相信这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获得)遇到了同样的问题 - 在按下搜索按钮后,用户界面首先冻结 - 一段时间后,它在加载下一部电影时有响应。我相信它并没有在 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);
        }
    }
4

3 回答 3

15

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

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

我认为您看到的结果是由于从您的机器创建和发送请求需要一些时间。首先,当完成后,执行DownloadStringTaskAsync可以等待网络 IO 和远程服务器完成,然后可以将执行返回给您。

另一方面,您的RunOrdinaryTask方法只是初始化一个任务并给它一个工作负载,并告诉它开始。然后它立即返回。这就是为什么您在使用RunOrdinaryTask.

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

  • 异步 != 并行

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

编辑

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

    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。

于 2010-10-30T09:37:14.237 回答
5

您确定该问题与从 IE/Registry/Somewhere Slow 检测到的代理配置设置无关吗?

尝试设置 webClient.Proxy = null (或在 app.config 中指定设置),您的“阻塞”期应该最短。

于 2011-02-02T15:58:23.173 回答
2

你是按 F5 还是 CTLR+F5 来运行它?使用 F5 时,VS 会延迟搜索 AsyncCtpLibrary.dll 的符号...

于 2010-10-30T19:22:37.727 回答