Добавить очереди операций PBUS/SBUS, общую консоль, настройки Candle и SLCAN, декодеры DSView и обработку плотных графиков. Обновить протокол Бальзам и проверки общих компонентов.
30 lines
4.6 KiB
Python
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() # Последняя проверка не допускает публикации результата, отменённого в конце короткого блока.
|