Files
templates/python/set_devices/background_work.py
T
Andrey a5e46d4627 Расширить обмен по шинам, CAN-адаптеры и обработку графиков
Добавить очереди операций PBUS/SBUS, общую консоль, настройки Candle и SLCAN, декодеры DSView и обработку плотных графиков. Обновить протокол Бальзам и проверки общих компонентов.
2026-10-17 14:36:14 +03:00

30 lines
4.6 KiB
Python

import time # Монотонные часы и короткое ожидание позволяют фоновому Python-коду регулярно освобождать GIL для обработчиков Qt.
class WorkBudget: # Длинный расчёт добровольно уступает процессор, чтобы поток интерфейса не ждал GIL после каждого вызова Qt.
def __init__(self, cancelled=None): # Необязательное threading.Event отменяет устаревшее задание без зависимости общего алгоритма от Qt.
self.cancelled = cancelled # Событие принадлежит одному заданию; его установку проверяем до и после уступки интерфейсу.
self.deadline = time.perf_counter() + .002 # Две миллисекунды непрерывной работы ограничивают конкуренцию с короткими Python-обработчиками GUI.
def __call__(self): # Рабочие проходы вызывают контрольную точку примерно раз в тысячу элементов, без проверки часов на каждом отсчёте.
if self.cancelled is not None and self.cancelled.is_set(): # Запрос отмены должен действовать даже до истечения бюджета вычислений.
raise InterruptedError("Фоновое задание отменено") # Вызывающий worker освобождает ресурсы и не публикует частичный результат.
if time.perf_counter() < self.deadline: # Несколько быстрых контрольных точек не должны создавать лишние ожидания.
return # Продолжаем текущую небольшую порцию вычислений до исчерпания её бюджета.
if self.cancelled is None: # Неотменяемая подготовка также обязана оставлять время главному потоку.
time.sleep(.001) # Пауза в одну миллисекунду освобождает GIL; sleep(0) не гарантирует времени для цепочки обработчиков Qt.
elif self.cancelled.wait(.001): # Ожидание освобождает GIL и немедленно заканчивается при отмене вместо полной паузы.
raise InterruptedError("Фоновое задание отменено") # Не продолжаем вычисление после отмены, полученной во время уступки интерфейсу.
self.deadline = time.perf_counter() + .002 # Новый бюджет начинается после ожидания, чтобы не накапливать пропущенные интервалы.
def work_items(items, checkpoint): # Оборачиваем полный последовательный проход без копирования и без потери исходных элементов.
if checkpoint is None: # Обычные синхронные вызовы сохраняют прежнее поведение и не получают искусственных задержек.
yield from items # Передаём значения в неизменном порядке без счётчика контрольных точек.
return # Ветвь без фонового бюджета не выполняет проверки часов.
for index, item in enumerate(items): # Счётчик определяет небольшие порции независимо от типа исходного контейнера.
if index % 1024 == 0: # Не более 1024 элементов между проверками ограничивают задержку отмены и удержание GIL.
checkpoint() # Уступаем время интерфейсу либо прерываем устаревшее задание.
yield item # Отдаём каждый исходный элемент ровно один раз, сохраняя точность и порядок измерений.
checkpoint() # Последняя проверка не допускает публикации результата, отменённого в конце короткого блока.