1

さまざまなタスクを実行するためにスレッドが生成されるマルチスレッドの Python アプリケーションがあります。このアプリケーションは何ヶ月もうまく機能していますが、最近問題が発生しました。

スレッドの 1 つは、集中的なデータ コピー コマンドを実行している Pythonsubprocess.Popenオブジェクトを開始します。

copy = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, preexec_fn = os.setsid, shell = False, close_fds = True)
if copy.wait():
  raise Exception("Unable to copy!")

copy コマンドが実行されている間、アプリケーション全体が最終的に停止し、他のスレッドが一度に数分間実行されることはありません。終了するとcopy、中断したところからすべてが再開されます。

これが起こらないようにする方法を見つけようとしています。私の最良の理論は、ATM は、カーネルがプロセスをスケジューリングする方法に関係しているということです。setsid()メインの Python アプリとは別にスケジュールされたコピー プロセスを取得するための呼び出しを追加しましたが、これは効果がありません。

copy.wait()関数が行うことはすべてwaitpid(). その1つのスレッドがGILを保持している間、呼び出しに時間がかかる可能性はありますか? もしそうなら、どうすればこれを防止/対処できますか? これをさらにデバッグするにはどうすればよいですか?

4

1 に答える 1

2

copy.wait()GILを持っていることも私の最初の疑いでした。ただし、これは私のシステムには当てはまらないようです (wait()呼び出しは他のスレッドの進行を妨げません)。

copy.wait()最終的にはos.waitpid(). 後者は、私の Linux システムでは次のようになります。

PyDoc_STRVAR(posix_waitpid__doc__,
"waitpid(pid, options) -> (pid, status)\n\n\
Wait for completion of a given child process.");

static PyObject *
posix_waitpid(PyObject *self, PyObject *args)
{
    pid_t pid;
    int options;
    WAIT_TYPE status;
    WAIT_STATUS_INT(status) = 0;

    if (!PyArg_ParseTuple(args, PARSE_PID "i:waitpid", &pid, &options))
        return NULL;
    Py_BEGIN_ALLOW_THREADS
    pid = waitpid(pid, &status, options);
    Py_END_ALLOW_THREADS
    if (pid == -1)
        return posix_error();

    return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status));
}

これにより、 POSIX でブロックされている間、明らかに GIL が解放されwaitpidます。

ハングしたときにプロセスにアタッチgdbしてpython、スレッドが何をしているかを確認します。おそらく、これはいくつかのアイデアを提供するでしょう。

編集これは、マルチスレッドの Python プロセスが でどのように見えるかですgdb:

(gdb) info threads
  11 Thread 0x7f82c6462700 (LWP 30865)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  10 Thread 0x7f82c5c61700 (LWP 30866)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  9 Thread 0x7f82c5460700 (LWP 30867)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  8 Thread 0x7f82c4c5f700 (LWP 30868)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  7 Thread 0x7f82c445e700 (LWP 30869)  0x00000000004a3c37 in PyEval_EvalFrameEx ()
  6 Thread 0x7f82c3c5d700 (LWP 30870)  0x00007f82c7676dcd in sem_post () from /lib/libpthread.so.0
  5 Thread 0x7f82c345c700 (LWP 30871)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  4 Thread 0x7f82c2c5b700 (LWP 30872)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  3 Thread 0x7f82c245a700 (LWP 30873)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
  2 Thread 0x7f82c1c59700 (LWP 30874)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
* 1 Thread 0x7f82c7a7c700 (LWP 30864)  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0

ここでは、2 つを除くすべてのスレッドが GIL を待機しています。典型的なスタック トレースは次のようになります。

(gdb) thread 11
[Switching to thread 11 (Thread 0x7f82c6462700 (LWP 30865))] #0  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
(gdb) where
#0  0x00007f82c7676b50 in sem_wait () from /lib/libpthread.so.0
#1  0x00000000004d4498 in PyThread_acquire_lock ()
#2  0x00000000004a2f3f in PyEval_EvalFrameEx ()
#3  0x00000000004a9671 in PyEval_EvalCodeEx ()
...

どのスレッドがどれであるかはhex(t.ident)、Python コードで表示することでわかります。どこtthreading.Threadオブジェクトがありますか。gdb私のシステムでは、これは( 0x7f82c6462700et al)に見られるスレッド ID と一致します。

于 2011-06-03T10:11:09.797 に答える