C# 异步等待 Task.Run 与 HttpClient.GetAsync
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13536505/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Async-await Task.Run vs HttpClient.GetAsync
提问by vondip
I'm new to c# 5's async feature. I'm trying to understand the difference between these two implementations:
我是 C# 5 的异步功能的新手。我试图了解这两种实现之间的区别:
Implementation 1:
实施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;
}
}
Implementation 2:
实施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;
}
}
}
I'd rather use the second implementation as it will require less 'async' signatures on methods in my code. I'm trying to understand what's the benefit of using the HttpClient class vs using a new Task and awaiting it instead?
我宁愿使用第二个实现,因为它在我的代码中的方法上需要更少的“异步”签名。我试图了解使用 HttpClient 类与使用新任务并等待它的好处是什么?
Is there any difference between the two implementations?
这两种实现之间有什么区别吗?
采纳答案by Jon Skeet
I'd rather use the second implementation as it will require less 'async' signatures on methods in my code.
我宁愿使用第二个实现,因为它在我的代码中的方法上需要更少的“异步”签名。
That sounds like a veryodd justification. You're trying to execute fundamentally "somewhat asynchronously" - so why not make that clear?
这听起来像是一个非常奇怪的理由。你试图从根本上“有点异步”地执行——那么为什么不说清楚呢?
Is there any difference between the two implementations?
这两种实现之间有什么区别吗?
Absolutely. The second implementation will tie up a thread while WebClient.DownloadStringblocks, waiting for the request to complete. The first version doesn't have any blocked threads - it relies on a continuation to fire when the request finishes.
绝对地。第二个实现会在WebClient.DownloadString阻塞时占用一个线程,等待请求完成。第一个版本没有任何阻塞的线程 - 它依赖于在请求完成时触发的延续。
Additionally, consider your Logger.Errorcall. In the async version, that will still execute in the context of the original calling code. So if this is in, say, a Windows Forms UI, you'll still be on the UI thread, and you can access UI elements etc. In the second version, you'll be executing in a thread pool thread, and would need to marshal back to the UI thread to update the UI.
此外,请考虑您的Logger.Error来电。在异步版本中,它仍将在原始调用代码的上下文中执行。因此,如果这是在 Windows 窗体 UI 中,您仍将在 UI 线程上,并且可以访问 UI 元素等。在第二个版本中,您将在线程池线程中执行,并且需要封送回 UI 线程以更新 UI。
Note that your async voidmethod almost certainly shouldn'tbe async void. You should only make an asyncmethod return voidfor the sake of complying with event handler signatures. In all other cases, return Task- that way the caller can see when your task has finished, handle exceptions etc.
请注意,您的async void方法几乎肯定不应该是async void. 您应该只为了遵守事件处理程序签名而使async方法返回void。在所有其他情况下,返回Task- 这样调用者可以看到您的任务何时完成,处理异常等。
Also note that you don't need to use HttpClientfor asynchrony - you could use WebClient.DownloadStringTaskAsyncinstead, so your final method could become:
另请注意,您不需要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;
}
}
}
回答by Cory Nelson
For server applications, asyncis about minimizing the number of blocked threads you've got: increasing the efficiency of the thread pool and perhaps allowing your program to scale to more users.
对于服务器应用程序,async是关于最大限度地减少阻塞线程的数量:提高线程池的效率,并可能允许您的程序扩展到更多用户。
For client applications where you're unlikely to need to care about thread count, asyncprovides a relatively easy way to keep your UI running fluid when you perform I/O.
对于您不太可能需要关心线程数的客户端应用程序,async提供了一种相对简单的方法来在您执行 I/O 时保持您的 UI 流畅运行。
It is much different from Task.Rununderneath the hood.
它Task.Run与引擎盖下面有很大不同。
回答by Harald Coppoolse
You will only benefit from async processing if your calling thread has something meaningful to do, like keeping the UI responsive. If your calling thread only starts a task and does nothing but wait until the task is finished, your process will run slower.
如果您的调用线程有一些有意义的事情要做,例如保持 UI 响应,那么您只会从异步处理中受益。如果您的调用线程只启动一个任务并且只等待任务完成,您的进程将运行得更慢。
Your second implementations starts a task, but you await for it to finish without doing anything else. This way you won't benefit.
你的第二个实现启动了一个任务,但你等待它完成而不做任何其他事情。这样你就不会受益。
You don't describe your environment, but if you have a UI that must keep responsive, then the the implementation of method 1 is ok, except that your Start() is not declared async and does not await:
您没有描述您的环境,但是如果您有一个必须保持响应的 UI,那么方法 1 的实现就可以了,只是您的 Start() 未声明为异步且不等待:
private async Task StartAsync()
{
foreach (var url in urls)
{
await ParseHtml(url)
}
}
You can call this from an event handler as follows:
您可以从事件处理程序中调用它,如下所示:
private async void OnButton1_clicked(object sender, ...)
{
await StartAsync();
}
Note: the ParseHtml has is preceded by await. The next html will be parsed after the previous parse has finished. However because the parse is async, the calling thread (the UI thread?) will be able to do other things like respond to user input.
注意:ParseHtml 前面有 await。上一个解析完成后,将解析下一个 html。然而,因为解析是异步的,调用线程(UI 线程?)将能够做其他事情,比如响应用户输入。
However, if your parseHTML function is able to run simultaneously the following code would be preferable, and probably faster:
但是,如果您的 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
}
- If you call a function that returns a Task, you can continue doing other things while the task is running or await for the task to finish and use the results of the task.
- if you await, your code stops, but your callers continue processing until they await for your task to complete
- you can only await in your procedure if your procedure is async.
- async functions can only be called by other async functions, or by calling Task.Run( () => ...) or if preferred: Task.Factory.StartNew( () => ...)
- Instead of void an async function returns Task
- Instead of TResult an async function return Task
<TResult> - The only exception is the event handler: declare it async and return void.
- If you need the task to finish, just await for the task.
- The return of the await is the TResult.
- 如果调用返回任务的函数,则可以在任务运行时继续做其他事情,或者等待任务完成并使用任务的结果。
- 如果您等待,您的代码会停止,但您的调用者会继续处理,直到他们等待您的任务完成
- 如果您的程序是异步的,您只能在您的程序中等待。
- 异步函数只能被其他异步函数调用,或者通过调用 Task.Run(() => ...) 或者如果首选:Task.Factory.StartNew(() => ...)
- 而不是 void 异步函数返回 Task
- 而不是 TResult 异步函数返回 Task
<TResult> - 唯一的例外是事件处理程序:声明它异步并返回 void。
- 如果您需要完成任务,只需等待任务。
- 等待的返回是 TResult。

