Python 中的错误? threading.Thread.start() 并不总是 return

Bug in Python? threading.Thread.start() does not always return

我有一个很小的 ​​Python 脚本,它(在我看来)使 threading.Thread.start() 行为异常,因为它不会立即 return。

在一个线程中,我想从一个基于 boost::python 的对象调用一个方法,该方法不会立即 return。

为此,我将 object/method 包装成这样:

import threading
import time
import my_boostpython_lib

my_cpp_object = my_boostpython_lib.my_cpp_class()

def some_fn():
    # has to be here - otherwise .start() does not return
    # time.sleep(1)  
    my_cpp_object.non_terminating_fn() # blocks

print("%x: 1" % threading.get_ident())
threading.Thread(target=some_fn).start()
print("%x: 2" % threading.get_ident())  # will not always be called!!

只要我 运行 在 my_cpp_object.non_terminating_fn() 之前编写一些代码,一切都正常。如果我不这样做,.start() 将以与直接调用 .run() 相同的方式阻塞。

在调用 boost::python 函数之前只打印一行是不够的,但是例如打印两行或调用 time.sleep() 使 start() return 立即如预期的那样。

你能解释一下这种行为吗?我将如何避免这种情况(除了在调用 boost::python 函数之前调用 sleep() 之外)?

这种行为(在大多数情况下,当您相信 interpreter/compiler 中存在错误时)不是 Python 中的错误,而是 竞争条件 由于 Python GIL(也讨论了 here),涵盖了您必须期望的行为。

一旦非Python函数my_cpp_object.non_terminating_fn()启动,GIL就不会被释放,直到它returns并阻止解释器执行任何其他命令。

所以 time.sleep(1) 在这里无济于事,因为 my_cpp_object.non_terminating_fn() 之后的代码在 GIL 发布之前不会执行。

boost::python 的情况下,当然如果您可以修改 C/C++ 部分,您可以按照 here.

所述手动释放 GIL

一个小例子(来自上面的 link)可能看起来像这样(在 boost::python 包装器代码中)

class scoped_gil_release {
public:
    inline scoped_gil_release() {
        m_thread_state = PyEval_SaveThread();
    }

    inline ~scoped_gil_release() {
        PyEval_RestoreThread(m_thread_state);
        m_thread_state = NULL;
    }

private:
    PyThreadState * m_thread_state;
};

int non_terminating_fn_wrapper() {
    scoped_gil_release scoped;
    return non_terminating_fn();
}