Python线程挂起

时间:2012-04-04 15:37:54

标签: python multithreading multiprocessing python-multithreading

我有一个遵循标准范例的简单线程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尝试了urllib2urllib3httplib2HTTP 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 ()

6 个答案:

答案 0 :(得分:13)

我写了一个模块,打印出在一个地方挂了10秒钟的线程。 hanging_threads.pypackage

以下是输出示例:

--------------------    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)