1.Ситуация
Я работаю над проектом на Python, и у меня довольно много функций следующего стиля:
from PyQt5.QtCore import *
import functools
...
def myfunc(self, callback, callbackArg):
'''
This function hasn't finished its job when it hits the
return statement. Provide a callback function and a
callback argument, such that this function will call:
callback(callbackArg)
when it has finally finished its job.
'''
def start():
myIterator = iter(self.myList)
QTimer.singleShot(10, functools.partial(process_next, myIterator))
return
def process_next(itemIterator):
try:
item = next(itemIterator)
except StopIteration:
finish()
# Do something
QTimer.singleShot(10, functools.partial(process_next, myIterator))
return
def finish():
callback(callbackArg)
return
start()
return
Эта функция не займет много времени для запуска, поэтому он не замораживает графический интерфейс и другие процессы.Вместо этого функция завершается почти сразу же и выполняет свою работу позже несколькими короткими пакетами.Наконец, когда работа выполнена, она вызывает предоставленный обратный вызов.
2.Проблема
Но есть и обратная сторона.Этот подход создает большую нагрузку на стек (я думаю), потому что вы получили следующую цепочку:
start() -> process_next() -> process_next() -> process_next() -> ... -> finish()
Хотя я не совсем уверен в этом.Функция process_next()
вызывает QTimer.singleShot(...)
и затем завершается.Так что, может быть, этой длинной цепочки в стеке вообще не происходит?
Знаете ли вы, если такой подход создает риск переполнения стека?Есть ли другие потенциальные риски, которые я еще не обнаружил?
РЕДАКТИРОВАТЬ
Спасибо @ygramoel за разъяснения.Таким образом, на самом деле, следующая строка:
QTimer.singleShot(10, functools.partial(process_next, myIterator))
вызывает функцию process_next(myIterator)
, не выдвигая другой кадр стека.Поэтому я не рискую переполнением стека длинными списками.Отлично!
Мне было просто интересно: иногда я не хочу задержки в несколько миллисекунд, как это предусмотрено функцией QTimer.singleShot()
.Чтобы немедленно вызвать следующую функцию (не нажимая другой кадр стека), я мог бы сделать:
QTimer.singleShot(0, functools.partial(process_next, myIterator))
Однако каждый вызов QTimer.singleShot()
запускает pyqtSignal()
.Запуск слишком большого количества из них за короткий промежуток времени расширяет основной поток до его пределов (помните: основной поток Python прослушивает входящие сигналы Pyqt).Основной поток обрабатывает записи очереди событий по очереди, вызывая соответствующие слоты.Поэтому, если программное обеспечение запускает слишком много событий в эту очередь, графический интерфейс может перестать отвечать на запросы.
Есть ли другой элегантный способ вызова process_next(myIterator)
без каких-либо из следующих проблем:
- Засорение очереди событий так, что графический интерфейс перестает отвечать на запросы.
- Переполнение стека рекурсивными функциональными кадрами.