我有一个很小的Python脚本(在我看来)会使threading.Thread.start()
出现意外行为,因为它不会立即返回。
在一个线程中,我想从基于boost::python
的对象调用一个方法,该方法不会立即返回。
为此,我将这样的对象/方法包装起来:
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()
按预期立即返回。
你能解释一下这种行为吗?我如何避免这种情况(除了在调用sleep()
函数之前调用boost::python
)?
答案 0 :(得分:3)
这种行为(在大多数情况下,当你相信解释器/编译器中的错误时)不是Python中的错误,而是竞争条件,涵盖了因Python而不得不期望的行为 GIL (还讨论了here)。
一旦启动了非Python函数my_cpp_object.non_terminating_fn()
,GIL就不会被释放,直到它返回并使解释器不执行任何其他命令。
所以time.sleep(1)
无论如何都没有帮助,因为在GIL发布之前,my_cpp_object.non_terminating_fn()
之后的代码不会被执行。
如果是boost::python
,当然如果您可以修改C / C ++部分,您可以按照here所述手动释放GIL。
一个小例子(来自上面的链接)看起来像这样(在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();
}