Приложение python не отвечает
Все библиотеки для разработки приложений работают с главным циклом, который обрабатывает такие события, как отображение окна на экране, его перемещение, изменение размера, реакция на нажатие кнопки. Словом, любое взаимодействие с интерфейсом.
Некоторые из этих событий могут быть связаны с функцией, которую мы предоставляем.
Например, метод button1_pressed(), который вызывается этой библиотекой, когда пользователь нажимает на элемент управления button1.
При работе с Qt способ реагирования на эти события обычно заключается в подключении сигнала к слоту.
Проблема возникает, когда в ответ на одно из этих событий или во время создания интерфейса мы выполняем операцию, длительность которой оказывается значительной (можно сказать, что это любая задача, занимающая более секунды).
Это приводит к тому, что процессор занят выполнением нашей задачи и не может заниматься основным циклом приложения.
В результате интерфейс перестает отвечать на запросы: мы не можем переместить его, закрыть, изменить размер или выполнить любой другой тип взаимодействия с ним.
Перейдем к конкретному примеру.
Следующий код дублирует окно с меткой (QLabel) и кнопкой (QPushButton), которая при нажатии загружает файл, используя стандартный модуль urllib.request.
Вы заметите, что во время загрузки, которая в моем случае длится около пяти секунд, интерфейс полностью замирает.
Разберем три решения этой же проблемы с их сильными и слабыми сторонами.
Первое решение: потоки
Это решение предполагает запуск нового потока для выполнения нашей трудоемкой задачи.
Поскольку основной цикл Qt выполняется в основном потоке программы, а наша операция выполняется во вторичном потоке, интерфейс остается активным, пока файл загружается в фоновом режиме.
Для этого мы используем класс QThread, который предоставляет кроссплатформенный API для создания потоков.
Ядром кода является класс Downloader, который наследуется от QThread и повторно реализует метод run() (строка 17), содержимое которого будет выполняться в новом потоке, когда мы создадим экземпляр и вызовем метод start() (строки 43 и 50).
В строке 49 мы соединяем сигнал finished, который отдает Qt, когда поток завершает выполнение, с нашим методом downloadFinished().
Хотя в примере речь идет о загрузке файла, этот метод позволяет переместить любую ресурсоемкую задачу в новый поток: просто поместите его внутрь метода run().
С другой стороны, многопоточное программирование должно выполняться с особой осторожностью.
Только метод run() выполняется в новом потоке, в то время как все остальные (включая сам Downloader.init()) выполняются в основном.
Кроме того, важно быть осторожным, чтобы не разделять объекты, к которым могут одновременно обращаться два или более потоков.
Второе решение: processEvents()
Альтернативой запуску нового потока является выполнение всей работы в основном потоке, но периодически позволяя Qt обрабатывать события приложения, чтобы интерфейс не перестал отвечать.
В предыдущих вариантах кода функция, которая выполняет тяжелую работу и блокирует выполнение на несколько секунд, была r.read().
Поскольку этот метод не возвращает нам контроль над программой, пока файл не будет полностью загружен, мы должны создать собственный цикл, который получает удаленный файл небольшими пакетами (128 байт) и в то же время позволяет Qt обрабатывать его события.
К счастью для нас, read() опционально принимает в качестве аргумента количество байт данных, которые мы хотим прочитать.
Здесь ключ лежит между строками 32-48, где строится цикл, который обрабатывает события Qt, потребляет кусок информации из сети и отправляет его в локальный файл, до тех пор, пока больше нет данных для извлечения.
Поскольку весь код выполняется в одном потоке, нам не нужно беспокоиться о проблемах, связанных с доступом к объектам и их модификацией, как в предыдущем случае.
Однако r.read(128) все равно является вызовом, который блокирует выполнение кода даже на очень короткое время, практически незаметное.
Если скорость интернет-соединения слишком низкая, даже извлечение этого небольшого количества байтов может привести к зависанию пользовательского интерфейса.
Третье решение: Twisted
Модуль qt5reactor позволяет объединить основные циклы Twisted и Qt в одном приложении, предоставляя нам доступ ко всему арсеналу асинхронных функций, предоставляемых сетевой библиотекой.
Для этого третьего решения мы также будем использовать библиотеку treq (похожую на Requests, но построенную на базе Twisted) для доступа к URL файла и загрузки содержимого.
Мы устанавливаем эти два инструмента просто с помощью pip:
Теперь код выглядит следующим образом:
Twisted, вероятно, является наиболее оптимальным решением, когда задачи, которые нам нужно выполнить, всегда связаны с доступом к какому-либо ресурсу в Интернете и часто встречаются в коде.
Здесь нам также не придется иметь дело с проблемами совместного использования объектов между потоками, поскольку Twisted всегда работает в главном потоке.
Те, кто немного разбирается в Twisted, найдут это решение весьма удачным.
И это действительно так: Qt и Twisted очень хорошо подходят друг другу благодаря своей структуре, философии и даже соглашениям об именах.
Заключение
Приведем основные моменты каждого из них.
Потоковая альтернатива эффективна для любых тяжелых задач, хотя мы уже говорили, что ее следует применять с осторожностью.
Серверные программисты больше всего боятся сбоев программы, но иногда программа не дает сбоев, но «не работает» уже достаточно страшно. Так называемое «не работает» означает, что программа больше не отвечает на новые запросы и находится в состоянии самоанализа. В английском очень яркое, но слово «завис», но я не знаю, как его перевести, назовем это «Застрял». Я столкнулся с двумя ситуациями: одна застревает в системных вызовах, таких как обычный дисковый ввод-вывод или сеть, многопоточная блокировка; другая - код входит в бесконечный цикл.
В статье «Искусство ведения журнала» обсуждается важность журнала. Если журнал подходит, он также может помочь нам решить проблему зависания программы. Если вы поспешите к повторяющимся журналам, это, скорее всего, будет бесконечным циклом.По содержимому журнала вы можете проанализировать местоположение бесконечного цикла и даже причину бесконечного цикла. Если нет вывода журнала, то посмотрите на содержимое последнего журнала, возможно, он скажет нам, что операция ввода-вывода вот-вот будет выполнена, конечно, он также может войти в бесконечный цикл.
Если журнал не может предоставить достаточную информацию, вам придется прибегнуть к другим средствам. В Linux, естественно, gdb выполняет мою роль. Функция gdb очень мощная. Серия программ отладки с GDB Чен Хао и Даниэля - лучшее, что я когда-либо видел, чтобы объяснить gdb. Хорошая китайская статья. CPython реализован на языке C, и его, естественно, можно отлаживать с помощью gdb. Однако по умолчанию отображается только стек C. Для таких смертных, как я, я не могу составлять стек python. В настоящее время мне нужно использовать python-dbg и libpython. Первый помогает отображать символическую информацию исходного кода Python, а второй позволяет нам отлаживать программы на уровне языка Python, например, распечатывать стек вызовов Python.
В этой статье кратко объясняется, как использовать gdb для анализа зависших программ в среде Linux. Python, используемый в этой статье, - Cpython2.7, а операционная система - Debian.
Программа зависает, вполне вероятно, что программа заблокирована, то есть ожидает окончания системного вызова, такого как дисковый ввод-вывод, сетевой ввод-вывод и многопоточность.По умолчанию многие системные вызовы заблокированы. Проблема многопоточности немного сложнее и будет рассмотрена позже. Вот пример сокета UDP (run_forever_block.py):
Это простая программа UDP. Код ожидает получения данных по адресу (0.0.0.0, 40000). Ядром является функция recvfrom в строке 10. Это блокирующий системный вызов, только когда данные читаются. Вернется, поэтому программа застрянет в строке 10. Конечно, вы также можете настроить эту функцию на неблокирование через fcntl.
Давайте посмотрим на ситуацию блокировки, запустим программу, а затем просмотрим статус этого процесса через верхнюю
Вы можете видеть, что pid этого процесса - 26466, статус процесса - S (Sleep), а CPU - 0.0. И статус процесса, и ЦП намекают нам, что текущий процесс блокирует системный вызов. На данный момент есть очень полезная команда: strace, которая может отслеживать все системные вызовы процесса, давайте посмотрим
Видно, что процесс застрял в системном вызове recvfrom, а соответствующий файловый дескриптор - 3. Фактически, имя recvfrom может приблизительно определить местонахождение проблемы. Что касается файловых дескрипторов, есть более практичная команда lsof (список открытых файлов), которая может видеть все файлы, открытые текущим процессом. В Linux все является файлом, естественно, что сокеты включены.
Из приведенного выше мы можем увидеть более подробную информацию об этом файловом дескрипторе (3U), например, IPV4 UDP, прослушивание порта 40000 и так далее.
Приведенный выше пример очень прост, настолько прост, что мы можем увидеть проблему непосредственно из системного вызова, но реальная ситуация может быть более сложной, то есть код, который не может напрямую определить местонахождение проблемы через системный вызов. В настоящее время вы можете использовать gdb. Чтобы узнать, как использовать gdb для отладки программ Python, вы можете обратиться к статье об использовании gdb для отладки процессов Python. Возьмем приведенный выше код в качестве примера, вы должны сначала подготовить условия
Прежде всего, обратитесь к DebuggingWithGdb, установите python-dbg в соответствии с вашей собственной системой Linux, а затем sudo apt-get install gdb python2.7-dbg на моей платформе (Debian)
Затем загрузите libpython и поместите его в свой домашний каталог.
Затем вы можете использовать gdb для анализа: gdb -p 26466
В интерактивной среде gdb, используя команду bt, вы можете увидеть стек C, который не имеет большого значения. Давайте посмотрим непосредственно на стек Python.
Бесконечный цикл очень раздражает, бесконечный цикл - это неожиданный бесконечный цикл. Наиболее типичный ожидаемый бесконечный цикл - это сервер сокетов, процесс не мертв, и служба не остановлена. Бесконечный цикл выглядит очень загруженным (CPU100%), но не имеет никакого реального эффекта. Бесконечные циклы имеют разную степень детализации. Самая грубая степень детализации - это взаимный вызов между двумя процессами, например RPC; второй - это уровень функции, и более распространенным является рекурсивный вызов без граничных условий или взаимный вызов между несколькими функциями. ; Наименьшая степень детализации - это бесконечный цикл определенного блока кода внутри функции, чаще всего используются операторы for, while. В Python уровень функции не вызывает бесконечного цикла, как показано в коде:
Запустите код, и вскоре будет сгенерировано исключение: RuntimeError: превышена максимальная глубина рекурсии. Очевидно, что внутри python поддерживается стек вызовов, который ограничивает максимальную глубину рекурсии, которая по умолчанию составляет около 1000. Вы также можете изменить максимальную глубину рекурсии с помощью sys.setrecursionlimit (limit). В Python, хотя этот вид бесконечного цикла на уровне функций не приведет к бесконечному циклу, он также займет драгоценный процессор, и этого никогда не должно происходить.
Бесконечный цикл на уровне блока кода заставит ЦП летать, например, следующий код
В этом случае он по-прежнему хорошо позиционируется, если смотреть на процессор.
По состоянию процесса R (выполнение) и 100% ЦП в принципе можно определить, что это бесконечный цикл.Разумеется, не исключено, что он требует интенсивной работы ЦП.Это связано со специфической логикой кода. В это время вы также можете посмотреть текущий стек вызовов через gdb. Конкретные приготовления такие же, как указано выше, а результат py-bt непосредственно приводится здесь.
В статье "Бесконечная" рекурсивная "программа на Python" упоминается, что использование гринлета сопрограмм может привести к эффекту бесконечного цикла, и это очень похоже на рекурсию функции, ее производительность и результаты отладки gdb такие же, как у бесконечного цикла.
Из-за Python GIL в нашем проекте не так часто используется многопоточность Python. Однако многопоточная взаимоблокировка - очень распространенная проблема, и, вообще говоря, это маловероятная вещь, ее нелегко воспроизвести, и она чаще всего возникает в онлайн-средах с высоким уровнем параллелизма. Здесь можно напрямую использовать код из «Elegant Python-Demonstrate the Generation of the Deadlock», а затем проанализировать зависшую многопоточную программу.
Пример кода блокировки потока
Запустив код, вы увидите, что процесс (номер процесса 26143) также находится в состоянии сна, потому что, по сути, процесс также заблокирован в системном вызове, поэтому вы также можете использовать strace
Как видите, процесс заблокирован в системном вызове futex, а значение параметров можно найти на странице руководства.
gdb также очень подходит для отладки многопоточных программ. Для многопоточности существует несколько часто используемых имен.
info thread: перечислить все темы и тему
поток x: переключиться на поток X
поток применить все bt: распечатать стек вызовов всех потоков
Ниже приведен упрощенный результат
Здесь я рекомендую очень хорошую статью, использующую gdb для отладки многопоточного кода python - запомните обнаружение взаимоблокировок, запишите проблему многопоточной взаимоблокировки, возникшую в реальной среде, заинтересованные студенты могут внимательно ее прочитать.
Когда процесс заблокирован владельцем, нам необходимо как можно скорее восстановить обслуживание. Причина для владельца карты может быть случайной или неизбежной. На практике настоящая причина для владельца карты процесса может быть не столь ясна. Когда мы анализируем конкретные причины владельца карты, нам может потребоваться как можно скорее перезапустить службу.В настоящее время нам нужно сохранить сцену, то есть coredump.
По моему опыту, есть два пути.
Первый тип, kill -11 pid, 11 представляет собой сигнал SIGSEGV, который генерирует coredump, убивая процесс, так что программа может быть быстро перезапущена, а затем медленно проанализирована.
Когда процесс больше не отвечает на новые запросы, сначала посмотрите журнал. Часто журнал содержит достаточно информации.
Во-вторых, посмотрите на информацию о процессе, состояние сна и 100% -ный ЦП может дать нам много информации, и вы также можете использовать pidstat для просмотра различной информации о процессе.
Если вы подозреваете, что процесс заблокирован, вы можете использовать strace для подтверждения
Под linux gdb - очень хорошее оружие отладки, рекомендуется пробовать чаще, coredump обязательно встретит
Программы Python, работающие под Linux, можно использовать с программами анализа python-dbg и libpython.
GDB: The GNU Project Debugger
Отладить программу с помощью GDB
Используйте gdb для отладки процессов Python
Используйте gdb для отладки многопоточного кода Python - запомните обнаружение тупиков
Ответить ниже «Ключевые слова» Для получения качественных ресурсов
Ответить на ключевое слово " pybook03 ", немедленно получите электронную версию" Think Python 2e ", переведенную домашней страницей и друзьями.
Ответить на ключевые слова " Начиная ", получите электронную версию 10 вводных книг по Python, собранных на домашней странице.
Ответить на ключевые слова " m ", сразу же получите сборник избранных высококачественных статей на Python
Ответить на ключевые слова " номер книги ", замените число на 0 и выше, есть подарок-сюрприз
Я написал программу в IDLE для токенизации текстовых файлов, и она начинает tokeniza 349 текстовых файлов! Как я могу остановить его? Как я могу остановить запущенную программу Python?
ОТВЕТЫ
Ответ 1
Чтобы остановить программу, просто нажмите Control + C .
Ответ 2
Вы также можете сделать это, если вы используете функцию exit() в своем коде. Более идеально, вы можете сделать sys.exit() . sys.exit() может завершить работу Python, даже если вы запускаете вещи параллельно через пакет multiprocessing .
Ответ 3
Ctrl-Break он более мощный, чем Ctrl-C
Ответ 4
- Чтобы остановить python script, просто нажмите Ctrl + C .
- Внутри script с exit() вы можете это сделать.
- Вы можете сделать это в интерактивном script с просто выходом.
- Вы можете использовать pkill -f name-of-the-python-script .
Ответ 5
Если ваша программа работает на интерактивной консоли, нажатие CTRL + C вызовет исключение KeyboardInterrupt в главном потоке.
Если ваша программа Python не перехватит ее, KeyboardInterrupt заставит Python завершить работу. Однако блок except KeyboardInterrupt: или что-то вроде пустого except: не позволит этому механизму фактически остановить выполнение сценария.
Однако эти механизмы в основном работают, только если интерпретатор Python работает и реагирует на события операционной системы. Если интерпретатор Python по какой-то причине не отвечает, наиболее эффективным способом является завершение всего процесса операционной системы, на котором выполняется интерпретатор. Механизм этого зависит от операционной системы.
В среде оболочки в стиле Unix вы можете нажать CTRL + Z , чтобы приостановить любой процесс, который в данный момент контролирует консоль. После того, как вы вернете приглашение оболочки, вы можете использовать jobs для отображения списка приостановленных заданий и убить первое приостановленное задание с помощью kill %1 . (Если вы хотите запустить его снова, вы можете продолжить работу на переднем плане, используя fg %1 ; для получения дополнительной информации прочтите руководство по оболочке по управлению заданиями.)
Кроме того, в Unix или Unix-подобной среде вы можете найти PID процесса Python (идентификатор процесса) и уничтожить его с помощью PID. Используйте что-то вроде ps aux | grep python ps aux | grep python для поиска запущенных процессов Python, а затем используйте kill <pid> для отправки сигнала SIGTERM .
В Windows у вас нет системы сигналов процессов Unix, но вы можете принудительно прервать запущенный процесс с помощью функции TerminateProcess . В интерактивном режиме самый простой способ сделать это - открыть диспетчер задач, найти процесс python.exe , соответствующий вашей программе, и нажать кнопку "Завершить процесс". Вы также можете использовать команду taskkill для аналогичных целей.
Ответ 6
Чтобы остановить запущенную программу, используйте CTRL + C, чтобы завершить процесс.
Чтобы обработать это программно в python, импортируйте модуль sys и используйте sys.exit() там, где вы хотите завершить программу.
Ответ 7
Ctrl + Z должен сделать это, если вы попали в оболочку Python. Имейте в виду, что экземпляры скрипта могут продолжать работать в фоновом режиме, поэтому в Linux вы должны будете завершить соответствующий процесс.
Ответ 8
Когда у меня работает скрипт Python на терминале Linux, CTRL +\работает. (не CRTL + C или D)
Ответ 9
прерывание клавиатуры, т.е. Control+C
Ответ 10
вы также можете использовать Activity Monitor , чтобы остановить процесс py
Ответ 11
Чтобы остановить вашу программу, просто нажмите CTRL + D
Ответ 12
Control + D работает для меня в Windows 10. Кроме того, добавление exit() в конце также работает.
Ответ 13
Если вы работаете с Spyder, используйте CTRL +. (DOT) и вы перезапустите ядро, также вы остановите программу.
Ответ 14
Чтобы остановить скрипт Python с помощью клавиатуры: Ctrl + C
Чтобы остановить его с помощью кода (это работает для меня на Python 3):
вы также можете использовать:
Ответ 15
Нажмите Ctrl + Alt + Delete , и появится диспетчер задач. Найдите команду Python, щелкните ее правой кнопкой мыши и выберите "Остановить" или "Убить".
Я с завидной регулярностью сталкиваюсь со всевозможными ошибками, так или иначе связанными с модулями Python. Существует огромное количество разнообразных модулей Python, которые разработчики активно используют, но далеко не всегда заботятся об установке зависимостей. Некоторые даже не удосуживаются их документировать. Параллельно существует две мажорные версии Python: 2 и 3. В разных дистрибутивах отдано предпочтение одной или другой версии, по этой причине самостоятельно установленную программу в зависимости от дистрибутива нужно при запуске предварять python или python2/python3. Например:
Причём обычно не происходит никаких проверок и угадали ли вы с выбором версии или нет вы узнаете только при появлении первых ошибок, вызванных неправильным синтаксисом программного кода для данной версии.
Также прибавляет путаницу то, что модули можно установить как из стандартного репозитория дистрибутивов, так и с помощью pip (инструмент для установки пакетов Python).
Цель этой заметки — рассмотреть некоторые характерные проблемы модулей Python. Все возможные ошибки вряд ли удастся охватить, но описанное здесь должно помочь понять, в каком примерно направлении двигаться.
Отсутствие модуля Python
Большинство ошибок модулей Python начинаются со строк:
В них трудно разобраться, поэтому поищите фразы вида:
- ModuleNotFoundError: No module named
- No module named
- ImportError: No module named
За ними следует название модуля.
Поищите по указанному имени в системном репозитории, или попробуйте установить командой вида:
Пакет Python установлен, но программа его не видит
Причина может быть в том, что вы установили модуль для другой версии. Например, программа написана на Python3, а вы установили модуль с этим же названием, но написанный на Python2. В этом случае он не будет существовать для программы. Поэтому нужно правильно указывать номер версии.
Команда pip также имеет свои две версии: pip2 и pip3. Если версия не указана, то это означает, что используется какая-то из двух указанных (2 или 3) версий, которая является основной в системе. Например, сейчас в Debian и производных по умолчанию основной версией Python является вторая. Поэтому в репозитории есть два пакета: python-pip (вторая версия) и python3-pip (третья).
В Arch Linux и производных по умолчанию основной версией является третья, поэтому в репозиториях присутствует пакет python-pip (третья версия) и python2-pip (вторая).
Это же самое относится к пакетам Python и самому Python: если версия не указана, значит имеется ввиду основная для вашего дистрибутива версия. По этой причине многие пакеты в репозитории присутствуют с двумя очень похожими названиями.
Установлена новая версия модуля, но программа видит старую версию
Я несколько раз сталкивался с подобными необъяснимыми ошибками.
Иногда помогает удаление модуля командой вида:
Также попробуйте удалить его используя системный менеджер пакетов.
Если модуль вам нужен, попробуйте вновь установить его и проверьте, решило ли это проблему.
Если проблема не решена, то удалите все файлы модуля, обычно они расположены в папках вида:
- /usr/lib/python2.7/site-packages/модуль
- /usr/lib/python3.7/site-packages/модуль
Ошибки, в которых присутствует слово AttributeError, NoneType, object has no attribute обычно вызваны не отсутствием модуля, а тем, что модуль не получил ожидаемого аргумента, либо получил неправильное число аргументов. Было бы правильнее сказать, что ошибка вызвана недостаточной проверкой данных и отсутствием перехвата исключений (то есть программа плохо написана).
В этих случаях обычно ничего не требуется дополнительно устанавливать. В моей практике частыми случаями таких ошибок является обращение программы к определённому сайту, но сайт может быть недоступен, либо API ключ больше недействителен, либо программа не получила ожидаемые данные по другим причинам. Также программа может обращаться к другой программе, но из-за ошибки в ней получит не тот результат, который ожидала, и уже это вызывает приведённые выше ошибки, которые мы видим.
Опять же, хорошо написанная программа в этом случае должна вернуть что-то вроде «информация не загружена», «работа программы N завершилась ошибкой» и так далее. Как правило, нужно разбираться с причиной самой первой проблемы или обращаться к разработчику.
Модуль установлен, но при обновлении или обращении к нему появляется ошибки
Это самая экзотическая ошибка, которая вызвана, видимо, повреждением файлов пакета. К примеру, при попытке обновления я получал ошибку:
При этом сам модуль установлен как следует из самой первой строки.
Проблема может решиться удалением всех файлов пакета (с помощью rm) и затем повторной установки.
К примеру в рассматриваемом случае, удаление:
После этого проблема с модулем исчезла.
Заключение
Пожалуй, это далеко не полный «справочник ошибок Python», но если вы можете сориентироваться, какого рода ошибка у вас возникла:
- отсутствует модуль
- модуль неправильной версии
- модуль повреждён
- внешняя причина — программа не получила ожидаемые данные
Так вот, если вы хотя бы примерно поняли главную причину, то вам будет проще понять, в каком направлении двигаться для её решения.
Читайте также: