Почему событие не запускается до завершения параллельных потоков?

#c# #.net #multithreading #events #parallel-processing

#c# #.net #многопоточность #Мероприятия #параллельная обработка

Вопрос:

Я получаю список элементов и помещаю их в ListView. Этот список может быть обширным, поэтому я использую параллельный.ForEach() для обработки построения ListObjects.

После создания экземпляра каждого элемента я запускаю событие в потоке пользовательского интерфейса, чтобы добавить элемент в ListView. Мое ведение журнала указывает мне, что события ожидают завершения параллельных задач, прежде чем продолжить с событием AddItem. Мне это не помогает. Мне нужно, чтобы дисплей обновлялся как можно быстрее, но если он все равно ожидает события, зачем запускать parallel?

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

Комментарии:

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

2. Хорошо, проблема была в этой параллели. ForEach не выполняется в отдельном потоке, отличном от того, в котором оно было запущено. Итак, он использовал поток пользовательского интерфейса (наряду с двумя другими), и любое планирование было выполнено после параллели. Цикл ForEach завершен. Как только я установлю параллель. ForEach внутри задачи. Factory.StartNew это поместило ForEach в другой поток, и работа была выполнена именно так, как я думал, что это должно быть.

3. Код, запрошенный выше: ` Задача. Factory.StartNew(() => { Параллельный. ForEach(FileSystemInfoList, ListItemOptions, (WorkItem) => { //ItemCreator_DoWork_2 тела задачи(WorkItem); }); }); ` ( Хорошо, как вы выполняете разметку кода?)

4. public void ItemCreator_DoWork_2(ListViewItem ) { … создайте специализированный элемент ListViewItem… Задача. Factory.StartNew(() => { OnAddItemProgress(новый AddItemProgressEventArgs(CurrentPercentage, новый список)); }, CancellationToken. Нет, TaskCreationOptions. Нет, uiScheduler); }

Ответ №1:

Да, цель Parallel.ForEach() — увеличить время завершения заполнения ListView. Но вам все равно придется дождаться полной загрузки ListView, прежде чем вы сможете его отобразить.

Если вам нужно что-то, что вы можете просмотреть во время его заполнения, вы можете попробовать ObservableCollection . Но вы должны быть осторожны; результирующая активность пользовательского интерфейса может снизить производительность вашего приложения.

Комментарии:

1. Хм, когда я использовал BackgroundWorker из 3.5, я мог вызвать событие просто отлично. Фоновый рабочий. Событие ProgressChanged для меня синхронизировалось бы обратно с потоком пользовательского интерфейса (вместо использования FromCurrentSynchronizationContext ), но это было единственное отличие. И это все еще не отвечает на мой первоначальный вопрос о том, почему событие ожидает завершения потоков перед запуском. Я дам классу, о котором вы упомянули, хороший подробный обзор, но это приложение WinForms, и я не уверен, насколько простой была бы реализация объекта WPF. Это, по крайней мере, дает мне пару идей.

2. Хотя пример, приведенный на странице MSDN, является приложением WPF, ObservableCollection не является (строго говоря) классом WPF. Его можно использовать где угодно.

3. Я не знаю точной технической причины, по которой оно ожидает, но когда вы думаете об этом, это имеет смысл. Что делает Parallel.For возможным, так это неявное предположение о безопасности потоков в рамках вашей более масштабной задачи; предполагается, что нет побочных эффектов или внешних сил, которые могли бы помешать завершению общей задачи. Другими словами, вы не можете просто войти в цикл во время его выполнения.

4. Как упоминалось выше, параллельный. ForEach не выполняется в другом потоке. Оно включает поток, в котором оно находится, в его планирование. Запустите задачу в новом потоке, а затем в параллельном. ForEach отключит поток пользовательского интерфейса.

Ответ №2:

Это не ответ на ваш вопрос, но я думаю, что это может быть улучшенным способом получения желаемых результатов.

Я нашел то, что искал. Это виртуальный ListView, он же ListView в VirtualMode.

Смотрите здесь:

http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.virtualmode.aspx