我有一个遵循标准范例的简单线程Python程序:
class SearchThread(threading.Thread):
def __init__(self, search_queue):
threading.Thread.__init__(self)
self.search_queue = search_queue
def run(self):
while True:
try:
search_url = self.search_queue.get(timeout=15)
# <do Internet search and print output/>
except Queue.Empty:
self.search_queue.task_done()
break
except Exception, e:
print e
if __name__ == '__main__':
search_queue = Queue.Queue()
for i in range(200):
t = SearchThread(search_queue)
t.setDaemon(True)
t.start()
search_queue.join()
队列中填充了大约1000个网址,并在HTTP GET
中执行了简单的<do Internet search and print output/>
。问题是在处理了大约500-700个条目(只需要几秒钟)之后,程序会一直挂起,没有输出,没有例外,没有任何内容。
我为requests
尝试了urllib2
,urllib3
,httplib2
,HTTP GET
,但没有任何变化。
如何调试挂起的线程Python程序?
BTW,我在Ubuntu 11.10(64位)下使用Python 2.7。
在挂起进程时盯着gdb跟踪时,我和以前一样无能为力 -
sudo gdb python 9602
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
...
(gdb) where
#0 0x00007fc09ea91300 in sem_wait () from /lib/x86_64-linux-gnu/libpthread.so.0
#1 0x00000000004ed001 in PyThread_acquire_lock ()
#2 0x00000000004f02de in ?? ()
#3 0x00000000004b6569 in PyEval_EvalFrameEx ()
#4 0x00000000004bcd2d in PyEval_EvalCodeEx ()
#5 0x00000000004b6a5b in PyEval_EvalFrameEx ()
#6 0x00000000004b6d77 in PyEval_EvalFrameEx ()
#7 0x00000000004bcd2d in PyEval_EvalCodeEx ()
#8 0x00000000004bd802 in PyEval_EvalCode ()
#9 0x00000000004dcc22 in ?? ()
#10 0x00000000004dd7e4 in PyRun_FileExFlags ()
#11 0x00000000004de2ee in PyRun_SimpleFileExFlags ()
#12 0x00000000004ee6dd in Py_Main ()
#13 0x00007fc09d86030d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
#14 0x000000000041cb69 in _start ()
答案 0 :(得分:13)
我写了一个模块,打印出在一个地方挂了10秒钟的线程。 hanging_threads.py(package)
以下是输出示例:
-------------------- 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()
当您忘记将另一个线程设置为守护程序时,会发生在主线程的退出处。
答案 1 :(得分:1)
您似乎面临与此主题中提到的相同的问题。
python multiprocessing: some functions do not return when they are complete (queue material too big)
Crux这是一个未解决/已关闭的错误? http://bugs.python.org/issue8237
答案 2 :(得分:1)
我不确定你是否还有问题(问题有些陈旧......)。
它看起来像是一个经典的死锁(因为它似乎挂在一些互斥锁上)。
对于GDB,存在一些不错的Python脚本,它们可以使Python调用的C回溯更具信息性。即它显示了实际的Python调用:
#3 0x00000000004b6569 in PyEval_EvalFrameEx ()
#4 0x00000000004bcd2d in PyEval_EvalCodeEx ()
#5 0x00000000004b6a5b in PyEval_EvalFrameEx ()
#6 0x00000000004b6d77 in PyEval_EvalFrameEx ()
#7 0x00000000004bcd2d in PyEval_EvalCodeEx ()
#8 0x00000000004bd802 in PyEval_EvalCode ()
我认为这些GDB Python脚本甚至包含在原始的Python发行版中。检查一下。
然后,有一个很棒的faulthandler module,它提供了一些打印Python回溯的功能(例如在信号处理程序中)。在my MusicPlayer project中,我对它们进行了一些扩展,我将它们大量用于调试。
例如,我添加了这个功能:
// This is expected to be called only from signal handlers (or in an evironment where all threads are stopped).
__attribute__((visibility("default")))
void _Py_DumpTracebackAllThreads(void) {
PyInterpreterState* interp = NULL;
PyThreadState* tstate = NULL;
// The current active Python thread (that might not be us).
tstate = _PyThreadState_Current;
// No Python state is currently active. Try to get our own, if we have one assigned.
if(!tstate)
tstate = PyGILState_GetThisThreadState();
// No thread found so far. Try the interpreter head.
if(!tstate)
interp = PyInterpreterState_Head();
if(!interp && tstate)
interp = tstate->interp;
if(!interp) {
printf("_Py_DumpTracebackAllThreads: no Python interpreter found\n");
return;
}
_Py_DumpTracebackThreads(STDOUT_FILENO, interp, tstate);
}
现在,当我在GDB或LLDB中并想知道当前的Python线程时,我只需输入p _Py_DumpTracebackAllThreads()
并将其打印在stdout上。
除此之外,您对所有当前线程的C回溯感兴趣,即t apply all bt full
左右应打印GDB中的所有回溯。
如果它是挂起的Python GIL,可能还有一些其他活动的Python线程挂起来用于其他事情。这是实际的错误。它应该在此之前发布Python GIL。
答案 3 :(得分:0)
你的while循环是无限的。即使队列为空,线程也永远不会完成执行。您应该检查队列中的新任务,或者通知线程(例如使用Event)没有任何任务。
答案 4 :(得分:0)
这个调试器可以调试多线程python程序:http://winpdb.org/
答案 5 :(得分:0)
另一件事是滥用Queue.get。第一个参数是布尔值'block'。 你应该输入类似的内容:
self.search_queue.get(timeout=15)
而且,正如我上面所写,避免使用无限循环。当超时到期时,Queue.get引发“Empty”异常,该异常由“Exception”除外(另一种应该避免使用的构造)。所以你的循环真的是无限的。你改变
的'除了例外'except Queue.Empty:
self.search_queue.task_done()
break
修改强>
初始问题代码如下
self.search_queue.get(15)