![](/img/trans.png)
[英]How can I make my python script wait until I tell it it to continue in the terminal?
[英]How can I tell where my python script is hanging?
所以我在调试我的 python 程序时遇到了一个使程序挂起的错误,就像处于无限循环中一样。 现在,我之前遇到了一个无限循环的问题,但是当它挂断时,我可以杀死程序,python 吐出一个有用的异常,告诉我当我向它发送 kill 命令时程序在哪里终止。 但是,现在,当程序挂断并按 ctrl-c 时,它不会中止而是继续运行。 有什么工具可以用来定位挂断电话吗? 我是分析的新手,但据我所知,分析器只能为您提供有关已成功完成的程序的信息。 或者您可以使用分析器来调试此类挂断吗?
让我们假设您正在运行您的程序:
python YOURSCRIPT.py
尝试将您的程序运行为:
python -m trace --trace YOURSCRIPT.py
并且在屏幕上打印很多东西时要有一些耐心。 如果您有一个无限循环,它将永远持续下去(停止问题)。 如果它卡在某个地方,那么大多数情况下你会卡在 I/O 上或者是一个死锁。
哇! 已经有 5 个答案,没有人提出最明显和最简单的答案:
print "**010"
、 print "**020"
等一样基本,贯穿主要区域。我编写了一个模块,可以打印出在一个地方挂起时间超过 10 秒的线程。 挂线程.py
跑:
python -m pip install hanging_threads
将此添加到您的代码中:
from hanging_threads import start_monitoring
start_monitoring(seconds_frozen=10, test_interval=100)
这是一个示例输出:
-------------------- Thread 5588 --------------------
File "C:\python33\lib\threading.py", line 844, in _exitfunc
t.join()
File "C:\python33\lib\threading.py", line 743, in join
self._block.wait()
File "C:\python33\lib\threading.py", line 184, in wait
waiter.acquire()
当您忘记将另一个线程设置为守护进程时,这会在主线程退出时发生。
如果您的程序太大而复杂,无法使用 pdb 单步执行或使用跟踪模块打印每一行,那么您可以尝试使用我在 8 位游戏编程时代的技巧。 从 Python 2.5 开始,pdb 能够使用commands
命令将代码与断点相关联。 您可以使用它来打印一条消息并继续运行:
(Pdb) commands 1
(com) print "*** Breakpoint 1 ***"
(com) continue
(com) end
(Pdb)
当断点 1 被击中时,这将打印一条消息并继续运行。 为其他几个断点定义类似的命令。
您可以使用它对您的代码进行一种二进制搜索。 在代码中的关键位置附加断点并运行它直到它挂起。 您可以从最后一条消息中知道哪个是它击中的最后一个断点。 然后您可以移动其他断点并重新运行以缩小代码中它挂起的位置。 冲洗并重复。
顺便说一句,在 8 位微型计算机(Commodore 64、Spectrum 等)上,您可以将一个值插入注册表位置以更改屏幕周围边框的颜色。 我曾经设置了几个断点来用不同的颜色来做到这一点,所以当程序运行时,它会给出一个迷幻的彩虹显示,直到它挂起,然后边框会变成单一颜色,告诉你最后一个断点是什么。 通过彩虹中每种颜色的数量,您还可以很好地了解不同代码部分的相对性能。 有时我会怀念这些新式“Windows”机器的简单性。
从 Python 3.3 开始,有一个内置的faulthandler模块:
import faulthandler
faulthandler.enable()
您也可以尝试http://code.activestate.com/recipes/576515-debugging-a-running-python-process-by-interrupting/ 。 只要 Python 进程没有屏蔽信号,它就应该可以工作,即使 Ctrl-C 不起作用,通常也是这种情况。
如果您的程序太复杂而无法简单地跟踪所有函数,您可以尝试运行它并手动将跟踪程序(如lptrace)附加到它。 它的工作方式有点像strace
它打印你的程序调用的每个函数。 调用方法如下:
python lptrace -p $STUCK_PROGRAM_PID
请注意,lptrace 需要运行 gdb。
没有什么比好的旧pdb
import pdb
pdb.run('my_method()',globals(),locals())
然后只需按 (n) 转到下一个命令, (s) 即可进入。 有关完整参考,请参阅文档。 按照你的程序一步一步来,你可能会很快弄清楚。
防止这些挂断比调试它们更容易。
首先: for
循环非常非常难以陷入循环不会终止的情况。 很难。
第二: while
循环相对容易陷入循环。
第一遍是检查每个while
循环,看它是否必须是一个while
循环。 通常,您可以将while
结构替换for
,并且您将通过重新思考循环来纠正您的问题。
如果您不能用for
替换while
循环,那么您只需证明while
语句中的表达式必须在每次循环中更改。 这并不难证明。
查看循环中的所有条件。 称其为T 。
查看循环体中的所有逻辑分支。 有没有办法在不改变条件T的情况下通过循环?
是的? 那是你的错误。 那条逻辑路径是错误的。
不? 太好了,该循环必须终止。
我有一个多线程守护进程,有时会在数小时后卡住,有时会在数周后卡住。 通过调试器运行它是不可行的,甚至可能没有帮助,因为调试多线程或多进程程序可能很痛苦。 通过跟踪运行它可能会在它卡住之前填满千兆字节,如果不是太字节的话。 第二次后台进程出现了挂,我想马上知道它在哪里,而不需要重新启动它,加入检查代码,通过调试器来运行它,并等待几小时,几天或几周的时间,为的情况下再次挂尚未进行调查。
我被pyrasite救了出来,它允许用户连接到正在运行的 Python 进程并交互式地检查帧(受此gist启发的示例):
$ pyrasite-shell 1071 # 1071 is the Process ID (PID)
Pyrasite Shell 2.0
Connected to '/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/python3.8 /opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py -n localhost /opt/pytroll/pytroll_inst/config/trollflow2.yaml'
Python 3.8.6 | packaged by conda-forge | (default, Dec 26 2020, 05:05:16)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(DistantInteractiveConsole)
>>> import sys
>>> sys._current_frames()
{139652793759488: <frame at 0x7f034b2c9040, file '<console>', line 1, code <module>>, 139653520578368: <frame at 0x7f034b232ac0, file '/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py', line 112, code __init__>}
第一帧没有信息; 那是我们自己的硫磷矿壳。 然而,第二帧显示当前我们的脚本卡在第 112 行的pyresample.spherical
模块中。我们可以使用回溯模块来获得完整的回溯:
>>> import traceback
>>> traceback.print_stack(list(sys._current_frames().values())[1])
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py", line 80, in <module>
main()
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py", line 75, in main
run(prod_list, topics=topics, test_message=test_message,
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/launcher.py", line 152, in run
proc.start()
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 121, in start
self._popen = self._Popen(self)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/context.py", line 277, in _Popen
return Popen(process_obj)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/popen_fork.py", line 19, in __init__
self._launch(process_obj)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/popen_fork.py", line 75, in _launch
code = process_obj._bootstrap(parent_sentinel=child_r)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/launcher.py", line 268, in process
cwrk.pop('fun')(job, **cwrk)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/plugins/__init__.py", line 403, in covers
cov = get_scene_coverage(platform_name, start_time, end_time,
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/plugins/__init__.py", line 425, in get_scene_coverage
return 100 * overpass.area_coverage(area_def)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollsched/satpass.py", line 242, in area_coverage
inter = self.boundary.contour_poly.intersection(area_boundary)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 494, in intersection
return self._bool_oper(other, -1)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 475, in _bool_oper
inter, edge2 = edge1.get_next_intersection(narcs2, inter)
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 326, in get_next_intersection
return None, None
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 298, in intersection
return None
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 264, in intersections
return (SCoordinate(lon, lat),
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 62, in cross2cart
return res
File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 112, in __init__
self.cart = np.array(cart)
我们可以使用 Python 内省的所有功能来检查堆栈,以帮助我们重建卡住的情况。
我自己没有使用过,但我听说Eric IDE很好并且有一个很好的调试器。 这也是我所知道的唯一一个带有 Python 调试器的 IDE
如果您的程序有多个线程,它可能会忽略 ctrl-c,因为一个线程连接到 ctrl-c 处理程序,但实时(失控?)线程对其充耳不闻。 CPython 中的 GIL(全局解释器锁)意味着通常任何时候实际上只有一个线程可以运行。 我想我用这个解决了我的(也许)类似的问题
i = 0
for t in threading.enumerate():
if i != 0:# and t.getName() != 'Thread-1':
print t.getName()
t._Thread__stop()
i += 1
一旦你知道线程的名称; 开始重新执行你的脚本并过滤它们,而不是阻止它们被中止。 i=0 条件防止主线程被中止。
我建议浏览并命名所有线程; 如:Thread(target=self.log_sequence_status, name='log status')
这段代码应该放在启动失控进程的主程序的末尾
哇 ! 似乎您一次添加了这么多代码而没有对其进行测试,以至于您无法说出在程序开始挂起之前添加了哪些代码......(最可能的问题原因)。
说真的,您应该分步编写代码并单独测试每个步骤(最好是 TDD)。
对于您发现正在运行的 python 代码和 ctrl-c 不起作用的确切问题,我将尝试一个原始猜测:您是否使用了一些except:
模糊地捕获所有异常。 如果您在循环中这样做(并在管理异常后继续循环),这很可能是 ctrl-c 不起作用的原因:它被此异常捕获。 更改为except Exception:
并且不应再捕获它(还有其他可能 ctrl+c 不像另一个海报建议的线程管理那样工作,但我相信上述原因更有可能)。
异常键盘中断
Raised when the user hits the interrupt key (normally Control-C or Delete).
在执行期间,会定期检查中断。 当内置函数 input() 或 raw_input() 等待输入时输入的中断也会引发此异常。 异常继承自 BaseException,以免被捕获 Exception 的代码意外捕获,从而阻止解释器退出。
Changed in version 2.5: Changed to inherit from BaseException.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.