简体   繁体   中英

How to run multiple tasks in parallel and update UI thread with results?

I have a method which consists of two lists (1. items to search and 2. workers to search with). Each worker takes an item from the list, searches for it, and add the results to a global results list which update the UI thread (a listview).

This is what I came up with so far:

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?
    }
}

The workers should search in parallel and their results added to the global results list. This list will then refresh the UI.

Am I on the right track with the above approach? Will the workers run in parallel? Should I update the UI thread within the task and use BeginInvoke ?

This will run parallel searches from the list items up to a specified number of workers without blocking the UI thread and then put the results into a list view.

    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());
    }

If your are working with Windows form, you can refer to How do I update the GUI from another thread?
If you are working with WPF. You can find your UI Dispatcher and use the dispatcher to update UI. Usually, even you try to update UI in a loop, it may not update the UI immediately. If you want to force to update UI, you can use DoEvents() method. The DoEvents() method also works for WPF. But try to avoid using DoEvents() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM