我不熟悉c# 5的异步功能。我正在尝试理解这两个实现之间的区别:
实现1:
private void Start()
{
foreach(var url in urls)
{
ParseHtml(url);
}
}
private async void ParseHtml(string url)
{
var query = BuildQuery(url); //BuildQuery is some helper method
var html = await DownloadHtml(query);
//...
MyType parsedItem = ParseHtml(html);
SaveTypeToDB(parsedItem);
}
private async Task<string> DownloadHtml(string query)
{
using (var client = new HttpClient())
try
{
var response = await client.GetAsync(query);
return (await response.Content.ReadAsAsync<string>());
}
catch (Exception ex)
{
Logger.Error(msg, ex);
return null;
}
}
实现2:
private void DoLoop()
{
foreach(var url in urls)
{
Start(url);
}
}
private async void Start(url)
{
await Task.Run( () => ParseHtml(url)) ;
}
private void ParseHtml(string url)
{
var query = BuildQuery(url); //BuildQuery is some helper method
var html = DownloadHtml(query);
//...
MyType parsedItem = ParseHtml(html);
SaveTypeToDB(parsedItem);
}
private string DownloadHtml(string query)
{
using (var client = new WebClient())
{
try
{
return client.DownloadString(query);
}
catch (Exception ex)
{
Logger.Error(msg, ex);
return null;
}
}
}
我宁愿使用第二个实现,因为它在我的代码中需要较少的“异步”签名。我正在尝试理解使用HttpClient类与使用新任务并等待它相比有什么好处?
这两种实现之间有什么区别吗?
发布于 2012-11-24 06:59:06
我宁愿使用第二个实现,因为它在我的代码中需要更少的方法的“异步”签名。
这听起来像是一个非常奇怪的理由。你试图从根本上“有点异步”地执行--所以为什么不把这一点说清楚呢?
这两种实现有什么不同吗?
绝对一点儿没错。第二个实现将在WebClient.DownloadString
阻塞时绑定一个线程,等待请求完成。第一个版本没有任何被阻塞的线程-它依赖于在请求完成时触发的延续。
此外,考虑您的Logger.Error
调用。在异步版本中,它仍将在原始调用代码的上下文中执行。因此,如果这是在Windows窗体UI中,那么您将仍然在UI线程上,并且您可以访问UI元素等。在第二个版本中,您将在线程池线程中执行,并且需要封送回UI线程来更新UI。
请注意,您的async void
方法几乎肯定不应该为async void
。您应该只为了符合事件处理程序签名的目的而使async
方法返回void
。在所有其他情况下,返回Task
-这样调用者就可以看到你的任务何时完成,处理异常等。
还请注意,您不需要使用HttpClient
进行异步--您可以使用WebClient.DownloadStringTaskAsync
,因此您的最后一个方法可以变成:
private async Task<string> DownloadHtmlAsync(string query)
{
using (var client = new WebClient())
{
try
{
return await client.DownloadStringTaskAsync(query);
}
catch (Exception ex)
{
Logger.Error(msg, ex);
return null;
}
}
}
发布于 2012-11-25 01:27:26
对于服务器应用程序,async
旨在最小化阻塞线程的数量:提高线程池的效率,也许还允许您的程序扩展到更多的用户。
对于不太可能需要关心线程数量的客户端应用程序,async
提供了一种相对简单的方法,可以在执行I/O时保持UI运行流畅。
它与底层的Task.Run
有很大的不同。
发布于 2015-08-10 19:53:07
只有当您的调用线程有一些有意义的事情要做时,您才能从异步处理中受益,比如保持UI响应。如果您的调用线程只启动一个任务,并且只等待任务完成,则您的进程将运行较慢。
您的第二个实现启动了一个任务,但是您等待它完成,而不做其他任何事情。这样你就不会受益了。
您没有描述您的环境,但是如果您有一个必须保持响应的UI,那么方法1的实现是可以的,除了您的Start()没有被声明为async并且不等待:
private async Task StartAsync()
{
foreach (var url in urls)
{
await ParseHtml(url)
}
}
您可以从事件处理程序中调用它,如下所示:
private async void OnButton1_clicked(object sender, ...)
{
await StartAsync();
}
注意: ParseHtml has的前面是await。下一个html将在前一个解析完成后被解析。然而,因为解析是异步的,所以调用线程(UI线程?)将能够做其他事情,如响应用户输入。
但是,如果您的parseHTML函数能够同时运行,则使用以下代码会更好,而且可能会更快:
private async Task StartAsync()
{
var tasks = new List<Task>()
foreach (var url in urls)
{
tasks.Add(ParseHtml(url));
}
// now you have a sequence of Tasks scheduled to be run, possibly simultaneously.
// you can do some other processing here
// once you need to be certain that all tasks are finished await Task.WhenAll(...)
await Task.WhenAll(tasks);
// Task.WhenAls(...) returns a Task, hence you can await for it
// the return of the await is a void
}
<
https://stackoverflow.com/questions/13536505
复制相似问题