Python与C++混合编程时子线程不运行的问题如何解决?

假设一个 Python 程序,内部有一个子线程。那么 C++调用该程序时,子线程不会自动运行。只有 C++在调用 Python 程序时,该子线程才会随之运行一小段时间。

示例 Python 程序 test_python_thread.py如下,调用的 c++代码test_python_thread.cpp附在后面。编译后直接运行./test_python_thread无输出,输入xxx后输出十个数字后停止。但按道理,python 代码应该会每秒钟输出一个数字。

我想知道怎么才能让 Python 里面的子线程能够不间断运行(每秒钟输出 1 个数字)?

#!/usr/bin/env python
# encoding: utf-8

import thread, time

class PyTest: def init(self): def local_python_thread(s): while True: print “local_python_thread”, s s += 1 time.sleep(1) thread.start_new_thread(local_python_thread, (0, ))

def xxx(self):
    s = 0
    while s < 10:
        print "cpp_thread", s
        s += 1
        time.sleep(1)

示例 C++程序 (test_python_thread.cpp

#include <python2.7/Python.h>
#include <boost/python.hpp>
#include <boost/filesystem.hpp>
#include <iostream>
#include <thread>

namespace bp = boost::python;

class CppTest { private: bp::object python_module; bp::object python_class; bp::object python_object; bp::object python_xxx;

public: CppTest() { Py_Initialize(); // 初始化

    PyRun_SimpleString("import sys");
    boost::filesystem::path path = "./test_python_thread.py";
    std::string chdir_cmd = std::string("sys.path.append(\"") + path.parent_path().c_str() + "\")";
    PyRun_SimpleString("sys.path.append('./')");
    PyRun_SimpleString(chdir_cmd.c_str());

    python_module = bp::import(path.stem().c_str());
    python_class = python_module.attr("PyTest");

    python_object = python_class();

    python_xxx = python_object.attr("xxx");
}

virtual void xxx()
{
        python_xxx();
}

};

int main() { CppTest test{};

std::string cmd;
while (true) {
    std::getline(std::cin, cmd);
    if (cmd == "xxx") {
        test.xxx();
    }
}

return 0;

}

编译 cpp(需要 boost 库):

g++ -std=c++0x -o test_python_thread  test_python_thread.cpp -lboost_system -l boost_filesystem -l python2.7 -g -I /usr/include/python2.7/ -l boost_python 

Python与C++混合编程时子线程不运行的问题如何解决?

14 回复

每太看懂啥意思,不知道是不是因为没有 join 线程的问题导致的.


这个问题我遇到过,核心是Python的GIL(全局解释器锁)和C++线程生命周期的冲突。

当你在C++扩展中创建子线程时,如果线程函数里要调用Python API(比如PyObject操作),必须在每个子线程里单独获取GIL。否则线程虽然创建了,但会被阻塞无法执行。

关键代码示例:

# 你的C++扩展模块代码中
#include <Python.h>
#include <thread>

// 线程函数
void worker_thread(PyObject* callback) {
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();  // 获取GIL
    
    // 现在可以安全调用Python API了
    PyObject_CallObject(callback, NULL);
    
    PyGILState_Release(gstate);  // 释放GIL
}

// 导出的Python函数
static PyObject* start_thread(PyObject* self, PyObject* args) {
    PyObject* callback;
    if (!PyArg_ParseTuple(args, "O", &callback))
        return NULL;
    
    Py_INCREF(callback);  // 增加引用计数
    
    std::thread t(worker_thread, callback);
    t.detach();  // 分离线程,让它在后台运行
    
    Py_RETURN_NONE;
}

然后在Python中初始化时要处理线程:

import your_cpp_module
import threading

# 确保线程支持已初始化
if not threading._is_main_thread():
    # 如果是从非主线程导入,需要调用PyEval_InitThreads()
    pass

# 现在可以调用C++函数启动线程了

总结:记得在每个C++线程里手动获取和释放GIL。

主进程退出之后,其所创建的所有子线程也会退出。python 创建子线程的时候,有一个属性里设置好之后,可以防止这个。好像是 SetDaemon(True) 这个属性。

主线程退出 ,daemon thread 也会退出 , 换成下面的方式试试。
t = threading.Thread(target=local_python_thread, args=(0, ))
t.start()
->
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

thread 已经是 deprecated 了
推荐的做法是,创建一个继承自 threading.Thread 的类的实例 obj.(需要实现 run()函数)
然后 obj.setDaemon(True); obj.start();
while threading.active() > 0:
pass
保证主进程不退出,后台线程得以正确运行

那个创建的线程直接被回收了吧……连引用都没。

主进程没有退出,一直在 getline 等待用户输入。

那个线程一直还在,只是没有运行。

每次输入 xxx 后会该线程会运行输出 10 个数。



C++ 代码没有调用 Python 解释器时,应该显式释放 GIL 来允许 Python 代码执行。

调用进 Python 代码前,应重新锁定 GIL。



关键词 PyGILState


这里有个说明:
https://docs.python.org/3/c-api/init.html#releasing-the-gil-from-extension-code
大概是说,你的程序在执行耗时操作,同时不调用 Python 的话这样写代码:
Py_BEGIN_ALLOW_THREADS
… Do some blocking I/O operation …
Py_END_ALLOW_THREADS
可以让 Python 的线程并行运行。

谢谢 点到位了。我面临的情况比示例要复杂,C++和 Python 里都有多线程,而且互相调用。我先去尝试一下,有问题再请教。

前面 Python Docs 里也有说 C++ 多线程该怎么做。

回到顶部