簡體   English   中英

異步等待 Task.Run 與 HttpClient.GetAsync

[英]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
}
  • 如果調用返回任務的函數,則可以在任務運行時繼續做其他事情,或者等待任務完成並使用任務的結果。
  • 如果您等待,您的代碼會停止,但您的調用者會繼續處理,直到他們等待您的任務完成
  • 如果您的程序是異步的,您只能在您的程序中等待。
  • 異步函數只能被其他異步函數調用,或者通過調用 Task.Run(() => ...) 或者如果首選:Task.Factory.StartNew(() => ...)
  • 而不是 void 異步函數返回 Task
  • 而不是 TResult 異步函數返回 Task <TResult >
  • 唯一的例外是事件處理程序:聲明它異步並返回 void。
  • 如果您需要完成任務,只需等待任務。
  • 等待的返回是 TResult。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM