我能在Python的后台线程上设置断点吗?

9

我正在使用Eclipse插件PyDev,并尝试在后台线程中设置断点。尽管代码正在执行,但断点从未被触发。这是一个小例子:

import thread

def go(count):
    print 'count is %d.' % count # set break point here

print 'calling from main thread:'
go(13)
print 'calling from bg thread:'
thread.start_new_thread(go, (23,))

raw_input('press enter to quit.')

那个示例中的断点在主线程调用时会被触发,但在后台线程调用时不会触发。我能做些什么吗?还是这是PyDev调试器的限制?
更新
感谢您提供的解决方法。我提交了一个PyDev功能请求,并已完成。它将在版本1.6.0中发布。谢谢,PyDev团队!
4个回答

12

问题在于thread模块中没有API能够知道线程何时启动。

在你的示例中,你可以像Alex指出的那样自己设置调试器跟踪函数,就像下面的代码一样(如果你不在远程调试器中,则当前需要 pydevd.connected = True -- 我将更改pydev使其不再需要)。你可能还希望为pydevd导入添加try..except ImportError(如果你不是在调试器中运行,则会失败)

def go(count):

   import pydevd
   pydevd.connected = True
   pydevd.settrace(suspend=False)
   print 'count is %d.' % count # set break point here
现在我再考虑一下,我认为 pydev 可以替换 thread 模块中的 start_new_thread 方法,并提供自己的函数来设置调试器,然后调用原始函数(我刚刚尝试了一下,似乎可以工作,所以如果你使用 nightly 版本,在几个小时后将可用,它应该可以正常工作,无需进行任何特殊设置)。

1
嘿,那太好了!感谢您提供的新功能。 - Don Kirkby
3
有趣的是,我在PyDev 2.6中也遇到了同样的问题。我有一个GUI应用程序,在该应用程序中,类A启动类B(QtCore.QThread),但是调试器不会停止在B方法中,尽管代码正在执行。如果我尝试进入b.start()方法,调试器会将我发送到“__main__”结构的末尾。 - Eugene Sajine
我无法解决pydevd(Python 2.7),所以这个解决方法不适用于我。 - Eugene Sajine
pydevd位于eclipse/plugins/org.python.pydev_XXX/pysrc。您必须将其添加到您的pythonpath中--在最新版本的PyDev中,有一个“pydevd”模板,它将把它添加到您的路径中,并执行settrace。 - Fabio Zadrozny
@Fabio,我正在使用PyDev 3.9,但在使用threading创建的后台线程中没有看到断点。我仍然需要使用pydevd.settrace(suspend=False)才能使其正常工作。你的修复程序是否也适用于使用threading创建的线程(而不是thread)? - studgeek

4
根本问题在于 sys.settrace,这是用于执行所有跟踪和调试的低级 Python 函数 -- 正如文档所说,

该函数是线程特定的;要支持多个线程的调试器, 必须使用 settrace() 为每个被调试的线程进行注册。

我认为当您在 PyDev 中设置断点时,结果的 settrace 调用总是发生在主线程上(我最近没有看过 PyDev,因此他们可能已经添加了一些解决方法,但我不记得从我查看的时间开始有任何方法)。
您可以自己实现一个解决方法,在设置了断点后,在主线程中使用 sys.gettrace 获取 PyDev 的跟踪函数,将其保存在全局变量中,并确保在所有感兴趣的线程中调用 sys.settrace 并将该全局变量作为参数 -- 稍微有些麻烦(对于在设置断点时已存在的线程来说更加麻烦!),但我想不到更简单的替代方案。

有时候这个会起作用。似乎我必须在不同的堆栈帧中调用sys.settrace(),而不是断点所在的堆栈帧。我还没有完全调查,但对我来说已经足够好了。谢谢。 - Don Kirkby
1
@Don,不客气——我猜测PyDev的跟踪功能可能与“不同帧”问题有关(虽然确切的原因并不明显——有一些可能性,但即使找到了也不一定能解决),但无论如何,我很高兴它能为你工作。请务必在PyDev的跟踪器中添加“在任何线程中断”的功能请求,以便他们知道这是需要的! - Alex Martelli

2

这个问题中,我找到了一种启动命令行调试器的方法:

import pdb; pdb.set_trace()

它不像Eclipse调试器那样易于使用,但总比没有好。


1
根据Fabio的帖子,对我来说这个方法有效,在使用setTrace("000.000.000.000")进行跟踪之后,其中的0是运行Eclipse/PyDev的计算机的IP地址。
threading.settrace(pydevd.GetGlobalDebugger().trace_dispatch)

它有效了!!谢谢。 我对pydevd上的帖子数量很少感到惊讶。 - Michael Dussere

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接