Как запустить несколько задач параллельно и обновить поток пользовательского интерфейса с результатами? - PullRequest
0 голосов
/ 03 октября 2018

У меня есть метод, который состоит из двух списков (1. элементы для поиска и 2. рабочие для поиска).Каждый работник берет элемент из списка, ищет его и добавляет результаты в глобальный список результатов, который обновляет поток пользовательского интерфейса (представление списка).

Это то, что я до сих пор придумал:

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

Рабочие должны выполнять параллельный поиск, а их результаты добавляются в глобальный список результатов.Этот список затем обновит пользовательский интерфейс.

Я на правильном пути с вышеуказанным подходом?Будут ли рабочие работать параллельно?Должен ли я обновить поток пользовательского интерфейса в задаче и использовать BeginInvoke?

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Это будет запускать параллельные поиски от элементов списка до указанного числа рабочих без блокировки потока пользовательского интерфейса, а затем помещать результаты в представление списка.

    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());
    }
0 голосов
/ 03 октября 2018

Если вы работаете с формой Windows, вы можете обратиться к Как обновить графический интерфейс из другого потока?
Если вы работаете с WPF.Вы можете найти свой Диспетчер пользовательского интерфейса и использовать диспетчер для обновления пользовательского интерфейса.Обычно, даже если вы пытаетесь обновить пользовательский интерфейс в цикле, он может не обновлять пользовательский интерфейс немедленно.Если вы хотите принудительно обновить пользовательский интерфейс, вы можете использовать метод DoEvents () .Метод DoEvents () также работает для WPF.Но старайтесь избегать использования DoEvents () .

...