【问题标题】:How to run multiple tasks in parallel and update UI thread with results?如何并行运行多个任务并使用结果更新 UI 线程?
【发布时间】:2019-03-08 04:57:53
【问题描述】:

我有一个方法,它包含两个列表(1. 要搜索的项目和 2. 要搜索的工作人员)。每个工作人员从列表中获取一个项目,搜索它,并将结果添加到更新 UI 线程(列表视图)的全局结果列表中。

这是我目前想出的:

List<Result> allResults = new List<Result>();
var search = new Search(workers);

//Will be full with items to search for
var items= new ConcurrentBag<item>();

while (items.Any())
{ 
    foreach (var worker in workers)
    {
        if (!items.Any())
            break;

        IEnumerable<Result> results = null;

        Task.Factory.StartNew(() =>
        {
            if (ct.IsCancellationRequested)
                return;

            items.TryTake(out Item item);
            if (item == null)
                return;

            results= search.DoWork(worker, item);
        }, ct);

        if (results?.Any() ?? false)
        {
            allResults.AddRange(reults);
        }

        //Update UI thread here?
    }
}

工作人员应该并行搜索,并将他们的结果添加到全局结果列表中。然后,此列表将刷新 UI。

我是否在上述方法的正确轨道上?工人会并行运行吗?我是否应该更新任务中的 UI 线程并使用BeginInvoke

【问题讨论】:

  • 您似乎正试图将项目分配给一系列工人。那正确吗?您正在尝试并行处理项目的平面列表?
  • @Enigmativity 是的。工作人员将为列表获取一个项目并异步处理它。直到列表为空为止。

标签: c# winforms async-await task


【解决方案1】:

如果您使用的是 Windows 窗体,您可以参考How do I update the GUI from another thread?
如果您正在使用 WPF。你可以找到你的 UI Dispatcher 并使用 Dispatcher 来更新 UI。通常,即使您尝试循环更新 UI,也可能不会立即更新 UI。如果要强制更新 UI,可以使用DoEvents() 方法。 DoEvents() 方法也适用于 WPF。但尽量避免使用DoEvents()

【讨论】:

  • Arggh - 永远不要打电话给DoEvents()。它仅在 VB6 兼容性的框架中。它绕过了消息循环,并可能导致令人讨厌的重入错误。
【解决方案2】:

这将在不阻塞 UI 线程的情况下从列表项到指定数量的工作人员运行并行搜索,然后将结果放入列表视图中。

    private CancellationTokenSource _cts;

    private async void btnSearch_Click(object sender, EventArgs e)
    {
        btnSearch.Enabled = false;
        lvSearchResults.Clear();
        _cts = new CancellationTokenSource();
        AddResults(await Task.Run(() => RunSearch(GetItems(), GetWorkerCount(), _cts.Token)));
        btnSearch.Enabled = true;
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        _cts?.Cancel();
    }

    private List<Result> RunSearch(List<Item> items, int workerCount, CancellationToken ct)
    {
        ConcurrentBag<List<Result>> allResults = new ConcurrentBag<List<Result>>();

        try
        {
            Parallel.ForEach(items, new ParallelOptions() { MaxDegreeOfParallelism = workerCount, CancellationToken = ct }, (item) =>
            {
                Search search = new Search(); // you could instanciate this elseware as long as it's thread safe...
                List<Result> results = search.DoWork(item);
                allResults.Add(results);
            });
        }
        catch (OperationCanceledException)
        { }

        return allResults.SelectMany(r => r).ToList();
    }

    private void AddResults(List<Result> results)
    {
        if (results.Count > 0)
            lvSearchResults.Items.AddRange(results.Select(r => new ListViewItem(r.ToString())).ToArray());
    }

【讨论】:

    猜你喜欢
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多