【问题标题】:Proper way to wait all tasks while still updating the UI thread在仍然更新 UI 线程的同时等待所有任务的正确方法
【发布时间】:2019-01-10 06:43:35
【问题描述】:

我正在尝试使用任务从多个 url 下载页面源,以一次下载多个站点。问题是我想在每个单独的任务完成时保持 UI 更新。当我尝试等待所有任务时,它会停止更新 UI,直到它们全部完成。这是我正在使用的当前代码。

编辑:我假设我被否决是因为我解释得不够好。我想一个更好的说法是为什么 continueWith 没有在 Task.WaitAll 之前运行。我希望 UI 在每次完成下载源时更新。一旦完成,列表框就会更新,让用户知道一切都已完成。

   private void btnGetPages_Click(object sender, EventArgs e)
    {

        for (int i = 1; i < 11; i++)
        {               
            string url = $"http://someURL/page-{i}.html";
            listBoxStatus.Items.Add($"Downloading source from {url}...");

            Task t = new Task(() =>
            {
                DownloadSource(url);
            });

            t.ContinueWith(prevTask => listBoxStatus.Items.Add($"Finished Downloading {url} source..."), TaskScheduler.FromCurrentSynchronizationContext());
            tasks.Add(t);
            t.Start();
        }

        Task.WaitAll(tasks.ToArray());
        listBoxStatus.Items.Add("All Source files have completed...");

    }

    private void DownloadSource(string url)
    {
        var web = new HtmlWeb();
        var doc = web.Load(url);
        pageSource += doc.Text;
    }

【问题讨论】:

  • 您使用的是哪个 .NET Framework 版本?您使用的是哪个版本的 C#?您在哪里看到使用构造函数创建Task 的实例并在其上调用Start 是一种好习惯?
  • @PauloMorgado 我正在使用 .net 4.6.1 Visual C# win forms 应用程序。我一直在浏览和阅读完成任务的不同方式。我对 C# 还很陌生,并没有真正了解 TPL 以及它是如何组合在一起的。
  • 您使用的是什么版本的 C#/Visual Studio?
  • @PauloMorgado 我正在使用 VS 2017 社区

标签: async-await task ui-thread


【解决方案1】:

您确实应该使用基于HttpClient 的异步下载方法,而不是您显示的同步方法。缺少那个,我会用这个:

private async Task DownloadSourceAsync(string url)
{
    await Task.Run(() => DownloadSource(url));

    listBoxStatus.Items.Add($"Finished Downloading {url} source...");
}

然后,您可以使您的 btnGetPages_Click 方法如下所示:

private async void btnGetPages_Click(object sender, EventArgs e)
{
    var tasks = new List<Task>();

    for (int i = 1; i < 11; i++)
    {
        string url = $"http://someURL/page-{i}.html";
        listBoxStatus.Items.Add($"Downloading source from {url}...");

        tasks.Add(DownloadSourceAsync(url));
    }

    Task.WaitAll(tasks.ToArray());
    listBoxStatus.Items.Add("All Source files have completed...");
}

【讨论】:

  • 我可以澄清一下...当我将任务添加到调用异步方法的列表中时,也会启动它们?
  • 我使用了您提供的代码,但它似乎没有运行任务。它将消息添加到列表框,然后什么也不做。我单步执行了代码,但似乎从来没有到达 downloadSourceAsync 方法。
  • 您是否尝试在DownloadSourceDownloadSourceAsync 上设置断点来判断它是否真的到达那里?
  • 是的,它正在使用这两种方法,但是它挂在等待 Task.Run() 上。我检查了源代码是否在我设置的 var 中,确实如此,所以我知道它已完成下载并到达站点。
最近更新 更多