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

时间:2015-07-16 14:03:20

标签: multithreading python-3.x boost-python

我有一个很小的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)?

1 个答案:

答案 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();
}