13

標準パラダイムに従って、単純なスレッド化された 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 個の URL で満たされ、単純なHTTP GET実行は で行われ<do Internet search and print output/>ます。問題は、500 から 700 のエントリ (数秒しかかからない) を処理した後、出力も例外も何もなく、プログラムが常にハングアップすることです。

requestsurllib2urllib3を試しましhttplib2HTTP GETが、何も変わりません。

ハングしているスレッド化された Python プログラムをどのようにデバッグしますか?

ところで、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 ()
4

6 に答える 6

16

ある場所で 10 秒以上ハングするスレッドを出力するモジュールを作成しました。 hang_threads.py (パッケージ)

出力例を次に示します。

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

これは、別のスレッドをデーモンとして設定するのを忘れたときに、メイン スレッドの終了時に発生します。

于 2013-07-19T11:13:52.347 に答える
2

まだ問題があるかどうかはわかりません(質問はやや古いです...)。

これは、古典的なデッドロックのように見えます (ミューテックス ロックでハングしているように見えるため)。

GDB には、Python 呼び出しを使用した C バックトレースをより有益なものにする優れた Python スクリプトがいくつかあります。つまり、これに対する実際の 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 ディストリビューションにも含まれていると思います。それらをチェックしてください。

次に、Python バックトレースを出力する機能を提供する素晴らしいfaulthandler モジュールがあります (例: シグナル ハンドラー)。私の MusicPlayer プロジェクトでは、それらを少し拡張し、デバッグに多用しています。

たとえば、次の関数を追加しました。

// 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 をリリースする必要がありました。

于 2014-02-22T13:50:55.060 に答える
1

このデバッガーは、マルチスレッドの Python プログラムをデバッグできます: http://winpdb.org/

于 2012-04-04T18:25:48.457 に答える
0

さらにもう1つは、Queue.getの誤用です。最初の引数はブール値'block'です。次のように入力する必要があります。

self.search_queue.get(timeout=15)

そして、私が上で書いたように、無限ループの使用は避けてください。タイムアウトが経過すると、Queue.getは「Empty」例外を発生させます。これは「exceptException」(使用を避ける必要がある別の構造)によってキャッチされます。したがって、ループは本当に無限です。あなたは「例外を除いて」を変更します

except Queue.Empty:
    self.search_queue.task_done()
    break

編集

最初の質問コードは次のようでした

self.search_queue.get(15)
于 2012-04-04T20:28:43.020 に答える
0

while ループは無限です。キューが空の場合でも、スレッドは決して実行を終了しません。新しいタスクのキューを確認するか、タスクが予期されていないことを (たとえばイベントを使用して) スレッドに通知する必要があります。

于 2012-04-04T16:19:31.703 に答える