[英]Async-await Task.Run vs HttpClient.GetAsync
我是 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 類與使用新任務並等待它的好處是什么?
這兩種實現之間有什么區別嗎?
我寧願使用第二個實現,因為它在我的代碼中的方法上需要更少的“異步”簽名。
這聽起來像是一個非常奇怪的理由。 你試圖從根本上“有點異步”地執行——那么為什么不說清楚呢?
這兩種實現之間有什么區別嗎?
絕對地。 第二個實現將在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;
}
}
}
對於服務器應用程序, async
是關於最大限度地減少阻塞線程的數量:提高線程池的效率,並可能允許您的程序擴展到更多用戶。
對於您不太可能需要關心線程數的客戶端應用程序, async
提供了一種相對簡單的方法來在您執行 I/O 時保持您的 UI 流暢運行。
它與Task.Run
下的Task.Run
有很大不同。
如果您的調用線程有一些有意義的事情要做,例如保持 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
>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.