Снова про потоки
-
- advanced
- Сообщения: 150
- Зарегистрирован: 05 ноя 2020, 08:26
- Версия LabVIEW: 18, 20.0f1
- Благодарил (а): 26 раз
- Поблагодарили: 5 раз
- Контактная информация:
Снова про потоки
Добрый день. Пишу программу сервера на основе тулкита Workers. Есть 6 этих самых worker'ов (классов), из которых рассмотрим 4 - UI, Acquisition, Registration и Network. Все классы наследуются от UI. В штатном режиме Acquisition опрашивает приборы и получает массивы данных (на это уходит 10мс), которые записывает в Shared Variable. Далее он же посылает события:
1. На отображение этих данных в UI
2. На запись их в Registration
Network же сам с частотой 2 Гц обращается к Shared Variable и отправляет данные дальше на отображение клиентами.
С помощью тех же Shared Variable записываю время на каждое из этих событий (время чтения с приборов, время между отправки по сети, время записи одного массива данных), и отображаю это время + данные в UI. Основные VI этих классов имеют тот же поток, что и UI. Конкретные VI по сбору, отправке и прочему я распределил на разные потоки, вызовы различных dll функций тоже вынес в отдельный поток. Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась? Спасибо.
1. На отображение этих данных в UI
2. На запись их в Registration
Network же сам с частотой 2 Гц обращается к Shared Variable и отправляет данные дальше на отображение клиентами.
С помощью тех же Shared Variable записываю время на каждое из этих событий (время чтения с приборов, время между отправки по сети, время записи одного массива данных), и отображаю это время + данные в UI. Основные VI этих классов имеют тот же поток, что и UI. Конкретные VI по сбору, отправке и прочему я распределил на разные потоки, вызовы различных dll функций тоже вынес в отдельный поток. Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась? Спасибо.
-
dadreamer
- professor
- Сообщения: 3962
- Зарегистрирован: 17 фев 2013, 16:33
- Награды: 4
- Версия LabVIEW: 2.5 — 2024
- Благодарил (а): 13 раз
- Поблагодарили: 138 раз
- Контактная информация:
Re: Снова про потоки
Рискну предположить (сильно санными тряпками не кидайтесь), что дело в свойстве Value: его вызов требует переключения UI-потока, который, как известно, один на всё приложение. Таскание за заголовок и аналогичные графические манипуляции также занимают UI-поток, отсюда и задержки в работе программы. Используйте локалки, если возможно, а если нет, то индексы (Get / Set Control Values by Index).Sergey Puzanov писал(а): ↑08 июл 2024, 15:22Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась?
- Вложения
-
- Block UI.vi
- lv2020
- (8.11 КБ) 29 скачиваний
Последний раз редактировалось dadreamer 08 июл 2024, 17:06, всего редактировалось 1 раз.
-
- advanced
- Сообщения: 150
- Зарегистрирован: 05 ноя 2020, 08:26
- Версия LabVIEW: 18, 20.0f1
- Благодарил (а): 26 раз
- Поблагодарили: 5 раз
- Контактная информация:
Re: Снова про потоки
Согласен, но задержки происходят в других потоках ведь. Та же запись в файл находится в other 1. Или UI по умолчанию имеет приоритет над всем остальным, и пока там всё не выполнится, остальные потоки ждут? Прикреплённый вами файл не смог открыть - говорит, что 2024 версия.
-
- VIP
- Сообщения: 1408
- Зарегистрирован: 03 фев 2010, 00:42
- Награды: 8
- Версия LabVIEW: 6.1 - 2025
- Откуда: Германия
- Благодарил (а): 1 раз
- Поблагодарили: 77 раз
- Контактная информация:
Re: Снова про потоки
Так и есть. Я в таких случаях просто вызываю Sleep из kernel32.dll в UI потоке, и смотрю на циклы, которые останавливаются. Ну вот, к примеру, на время блокировки UI потока оба цикла остановят обновление индикатора, но второй цикл будет всё равно бежать, а первый встанет колом:dadreamer писал(а): ↑08 июл 2024, 16:15Рискну предположить (сильно санными тряпками не кидайтесь), что дело в свойстве Value: его вызов требует переключения UI-потока, который, как известно, один на всё приложение.Sergey Puzanov писал(а): ↑08 июл 2024, 15:22Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась?

Это по значениям индикаторов хорошо видно:

-
dadreamer
- professor
- Сообщения: 3962
- Зарегистрирован: 17 фев 2013, 16:33
- Награды: 4
- Версия LabVIEW: 2.5 — 2024
- Благодарил (а): 13 раз
- Поблагодарили: 138 раз
- Контактная информация:
Re: Снова про потоки
А как организованы потоки? Это отдельные циклы? Если речь о Preferred Execution System, то это не совсем подходящий способ распараллеливания кода. Вот тут подробно расписано: viewtopic.php?p=54155#p54155 И даже если выставить Other 1, например, и вызвать там Property Node, этот вызываемый узел также потребует, чтобы UI-поток был свободен. То есть, смена системы исполнения не избавляет от подобного.Sergey Puzanov писал(а): ↑08 июл 2024, 16:36Согласен, но задержки происходят в других потоках ведь. Та же запись в файл находится в other 1.
Поторопился, залил не ту версию. Исправил. В принципе, Андрей там в теме выложил почти такой же пример.Sergey Puzanov писал(а): ↑08 июл 2024, 16:36Прикреплённый вами файл не смог открыть - говорит, что 2024 версия.
Ещё, будьте готовы к тому, что на не RTOS до конца избавиться от графических эффектов не получится. Вот длинный тред, где уже обсуждали всё это: Странное торможение параллельных потоков.
-
- professor
- Сообщения: 3564
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 57 раз
- Поблагодарили: 191 раз
- Контактная информация:
Re: Снова про потоки
Наследование и распределение по разным потокам противоречат друг другу. Как вы это сделали? Может, на самом деле нет?Sergey Puzanov писал(а): ↑08 июл 2024, 15:22 Все классы наследуются от UI.
Конкретные VI по сбору, отправке и прочему я распределил на разные потоки
-
- professor
- Сообщения: 3564
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 57 раз
- Поблагодарили: 191 раз
- Контактная информация:
-
dadreamer
- professor
- Сообщения: 3962
- Зарегистрирован: 17 фев 2013, 16:33
- Награды: 4
- Версия LabVIEW: 2.5 — 2024
- Благодарил (а): 13 раз
- Поблагодарили: 138 раз
- Контактная информация:
Re: Снова про потоки
Не дёргают, посмотрите


-
- advanced
- Сообщения: 150
- Зарегистрирован: 05 ноя 2020, 08:26
- Версия LabVIEW: 18, 20.0f1
- Благодарил (а): 26 раз
- Поблагодарили: 5 раз
- Контактная информация:
Re: Снова про потоки
Тоже есть подозрение, что оно не работает. Но так да - это параллельные циклы со своими структурами case. Сама VI с циклом в потоке same as caller, а те что в кейсах - те в других потоках, по крайней мере по настройкам. Буду завтра экспериментировать и отпишусьArtem.spb писал(а): ↑08 июл 2024, 17:28Наследование и распределение по разным потокам противоречат друг другу. Как вы это сделали? Может, на самом деле нет?Sergey Puzanov писал(а): ↑08 июл 2024, 15:22 Все классы наследуются от UI.
Конкретные VI по сбору, отправке и прочему я распределил на разные потоки
-
- professor
- Сообщения: 3564
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 57 раз
- Поблагодарили: 191 раз
- Контактная информация:
Re: Снова про потоки
Даже не знаю, что проще. Делать event для пересылки значений, или дёргать по индексам. С индексами легко промахнуться, но можно сначала их искать по именам. Но тут снова приходим к текстовому стилю программирования. Но штука интересная.
-
- professor
- Сообщения: 3564
- Зарегистрирован: 31 июл 2011, 23:05
- Награды: 2
- Версия LabVIEW: 12-18
- Благодарил (а): 57 раз
- Поблагодарили: 191 раз
- Контактная информация:
Re: Снова про потоки
Ну вот это вот фигня.Sergey Puzanov писал(а): ↑08 июл 2024, 17:52 Сама VI с циклом в потоке same as caller, а те что в кейсах - те в других потоках, по крайней мере по настройкам. Буду завтра экспериментировать и отпишусь
На передачу данных между потоками тратиться много времени. По крайней мере так было на заре цивилизации, и в моих тестах я обнаружил потерю производительности. В другом потоке должна быть вся "большая" функция, а не те кого она дёргает.
-
- advanced
- Сообщения: 150
- Зарегистрирован: 05 ноя 2020, 08:26
- Версия LabVIEW: 18, 20.0f1
- Благодарил (а): 26 раз
- Поблагодарили: 5 раз
- Контактная информация:
Re: Снова про потоки
Отображение данных прямиком в индикаторы проблему не решило, следующий шаг будет всё таки удаление наследования, чтобы каждый цикл в своём потоке старался работать.
-
- VIP
- Сообщения: 1408
- Зарегистрирован: 03 фев 2010, 00:42
- Награды: 8
- Версия LabVIEW: 6.1 - 2025
- Откуда: Германия
- Благодарил (а): 1 раз
- Поблагодарили: 77 раз
- Контактная информация:
Re: Снова про потоки
Как дополнение к ссылке, что выше приводилась, просто что б лучше понимать как оно работает.Sergey Puzanov писал(а): ↑08 июл 2024, 16:36Согласен, но задержки происходят в других потоках ведь. Та же запись в файл находится в other 1.
standard, instrument I/O, data acquisition, other 1 и other 2 в общем ничем не отличаются. Чуть особняком стоит standard в том смысле, что если Top Level VI помечен как "same as caller", то он будет выполняться в standard.
По умолчанию LabVIEW выделяет вам двадцать четыре потока на каждую систему исполнения и каждый приоритет. Это легко показать.
Допустим я сделаю реентерантный инструмент, в который брошу Sleep из kernel32.dll с задержкой в секунду и потокобезопасным вызовом:

Нвстройки этого подприбора:

Вызову его вот так и замеряю время исполнения:

Одна секунда, кто б сомневался.
Теперь смотрите, я положу двадцать четыре копии:

По-прежнему одна секунда. Но стоит мне положить двадцать пятую копию, то ситуация изменится:

Стало две секунды. Это потому, что мы израсходовали пул потоков. Теоретически можно внутри DLL вызвать GetThreadID и увидеть, что один из 25-и вернёт нам один из ID, используемых ранее.
Теперь допустим, что нам надо больше потоков. Я сделаю ещё пару таких же инструментов с задержкой, но попрошу один исполняться в other1, а второй в other2, при этом положу 24 копии каждого, вот теперь у меня 48 потоков:

И время исполнения по-прежнему одна секунда. Стоит лишь мне добавить двадцать пятый (неважно other1 или other2), и снова станет две секунды (можете сами проверить).
Важно также понимать, что пул потоков выделяется не только на систему исполнения, но и на приоритет.
Проверяем - делаем два инструмента, оба будут в other1, но один будет иметь нормальный приоритет, а второй - повышенный, разница будет лишь вот тут:

И вот у нас снова 48 потоков и время одна секунда, хотя оба в other1, но приоритет у них разный:

Таким образом в нашем распоряжении овердофига потоков. Переключать инструменты в другие системы исполнения имеет смысл лишь тогда, когда пул потов полностью израсходован (хотя это не так просто проверить).
Ещё такой момент - в принципе пул потков настраиваемый. Допустим, нам отчаянно не хватает тех, что выделены по умолчанию. Можно добавить в LabVIEW.ini следующие строки:
Код: Выделить всё
ESys.StdNParallel=-1
ESys.other1.Normal=1024

Но мне руками больше нравится.
Перезапускаем LabVIEW и проверяем (я отчаянно не ленивый сегодня):

И вот - одна секунда на всё про всё:

Task Manager тоже показывает больше тысячи потоков:

Там есть ещё пара небольших тонкостей, скажем по умолчанию изначально вам выделяется столько потоков, сколько у вас ядер на процах, а потом оно довыделяется по необходимости (и вроде бы пачками по четыре потока), но не более 24-х потоков на систему и приоритет.
Поэтому при первом запуске инструмента, который требут 24-х потоков на 16-ти процессорной тачке я вижу не одну секунду, а чуть больше, где-то 1.2, двести миллисекунд — это накладные расходы на создание потоков по запросу, но это только один раз происходит, дальше эти созданные потоки так и висят в памяти, но это уже совсем тонкости. Если количество потоков задавать индивидуально, типа ESys.Normal=64, то потоки будут создаваться при загрузке VI, а если глобально через ESys.StdNParallel=64, то ри запуске (что даст задержку), но это неточно.
Полный список ключей для управления пулом потоков вот:
Код: Выделить всё
ESys.StdNParallel
ESys.Bgrnd
ESys.Normal
ESys.High
ESys.TCritical
ESys.Vhigh
ESys.DAQ.Bgrnd
ESys.DAQ.High
ESys.DAQ.Normal
ESys.DAQ.TCritical
ESys.DAQ.VHigh
ESys.instrument.Bgrnd
ESys.instrument.High
ESys.instrument.Normal
ESys.instrument.TCritical
ESys.instrument.VHigh
ESys.other1.Bgrnd
ESys.other1.High
ESys.other1.Normal
ESys.other1.TCritical
ESys.other1.VHigh
ESys.other2.Bgrnd
ESys.other2.High
ESys.other2.Normal
ESys.other2.TCritical
ESys.other2.VHigh
Код: Выделить всё
ESys.StdNParallel=64
-
- advanced
- Сообщения: 150
- Зарегистрирован: 05 ноя 2020, 08:26
- Версия LabVIEW: 18, 20.0f1
- Благодарил (а): 26 раз
- Поблагодарили: 5 раз
- Контактная информация:
Re: Снова про потоки
Благодарю за такой развёрнутый ответ, стало намного понятнее. Так же самостоятельно провёл тест и убедился в том, что передача данных между потоками критично замедляет выполение функций, и, соответственно, разделять нужно независимые (или слабозависимые) части программы. Попробую скомбинировать все эти знания.
-
- advanced
- Сообщения: 150
- Зарегистрирован: 05 ноя 2020, 08:26
- Версия LabVIEW: 18, 20.0f1
- Благодарил (а): 26 раз
- Поблагодарили: 5 раз
- Контактная информация:
Re: Снова про потоки
С учётом ваших ответов и небольшим перераспределением удалось добиться стабильных 30-50мс вместо 120-300мс+, всем спасибо!