由于某些原因,以下程序启动后会暂停。我相信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篇有关延续传递样式的文章,这实际上就是async
和await
的意思。如果您想详细了解新功能,则可能需要阅读Eric关于CPS和Async的文章。无论如何,以上两个链接在解释一个非常重要的事实方面都做得很好:
换句话说,
async
和await
不会为您增加新线程。它们只是让您在执行阻塞操作时恢复正常流程的执行-在这种情况下,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一起玩。