我是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类与使用新任务并等待它的好处是什么?
两种实现之间有什么区别吗?
答案 0 :(得分:28)
我宁愿使用第二个实现,因为它在我的代码中需要更少的“异步”签名。
听起来像非常奇怪的理由。你试图从根本上“稍微异步”地执行 - 所以为什么不说清楚呢?
两种实现之间有什么区别吗?
绝对。第二个实现将在WebClient.DownloadString
块时占用一个线程,等待请求完成。第一个版本没有任何阻塞的线程 - 它依赖于在请求完成时触发的延续。
此外,请考虑Logger.Error
来电。在异步版本中,它仍将在原始调用代码的上下文中执行。因此,如果它位于Windows窗体UI中,您仍将位于UI线程上,并且您可以访问UI元素等。在第二个版本中,您将在线程池线程中执行,并且需要编组回UI线程以更新UI。
请注意,您的async void
方法几乎肯定不应为async void
。为了遵守事件处理程序签名,您应该只返回async
方法返回void
。在所有其他情况下,返回Task
- 调用者可以看到您的任务何时完成,处理异常等。
另请注意,您不需要使用HttpClient
进行异步 - 您可以使用WebClient.DownloadStringTaskAsync
代替,因此您的最终方法可能会变为:
private async Task<string> DownloadHtml(string query)
{
using (var client = new WebClient())
{
try
{
return await client.DownloadStringTaskAsync(query);
}
catch (Exception ex)
{
Logger.Error(msg, ex);
return null;
}
}
}
答案 1 :(得分:6)
对于服务器应用程序,async
是关于最小化您获得的被阻止线程的数量:提高线程池的效率,并允许您的程序扩展到更多用户。
对于您不太可能需要关心线程计数的客户端应用程序,async
提供了一种相对简单的方法,可以在执行I / O时保持UI运行流畅。
它与引擎盖下的Task.Run
大不相同。
答案 2 :(得分:1)
如果您的调用线程有一些有意义的事情要做,比如保持UI响应,您将只能从异步处理中受益。如果你的调用线程只启动一个任务而只是等到任务结束,那么你的进程将运行得更慢。
你的第二个实现启动了一个任务,但你等待它完成而不做任何其他事情。这样你就不会受益。
您没有描述您的环境,但是如果您的UI必须保持响应,那么方法1的实现是可以的,除了您的Start()未声明为异步且不等待:
private async Task StartAsync()
{
foreach (var url in urls)
{
await ParseHtml(url)
}
}
您可以从事件处理程序中调用它,如下所示:
private async void OnButton1_clicked(object sender, ...)
{
await StartAsync();
}
注意:ParseHtml之前有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
}
<TResult
&gt;