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();
}
我有一个很小的 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.
一个小例子(来自上面的 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();
}