由于Python解释器有全局解释所GIL的原因,导致在同一时刻只能有一个线程拥有解释器,所以在C++多线程调用python脚本时,需要控制GIL,线程获取GIL。
所以一个程序里,无论有多少条线程调用python,python只能初始化一次。
每次线程要调用python时,都要拥有GIL。
//python只需要初始化一次
void PythonHandler::PythonInit()
{
if( !Py_IsInitialized() )
{
//1.初始化Python解释器,这是调用操作的第一步
Py_Initialize();
if( !Py_IsInitialized() ){
qDebug("初始化Python解释器失败");
}else{
// 初始化线程支持
PyEval_InitThreads();
// 启动子线程前执行,为了释放PyEval_InitThreads获得的全局锁,否则子线程可能无法获取到全局锁。
PyEval_ReleaseThread(PyThreadState_Get());
qDebug("初始化Python解释器成功");
}
}
}
//将全局解释器锁和线程的相关操作用类封装
#ifndef PYTHREADSTATELOCK_H
#define PYTHREADSTATELOCK_H
#include "Python.h"
class PyThreadStateLock
{
public:
PyThreadStateLock(void)
{
_save = nullptr;
nStatus = 0;
nStatus = PyGILState_Check() ; //检测当前线程是否拥有GIL
PyGILState_STATE gstate;
if ( !nStatus )
{
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
nStatus = 1;
}
_save = PyEval_SaveThread();
PyEval_RestoreThread(_save);
}
~PyThreadStateLock(void)
{
_save = PyEval_SaveThread();
PyEval_RestoreThread(_save);
if (nStatus)
{
PyGILState_Release(gstate); //释放当前线程的GIL
}
}
private:
PyGILState_STATE gstate;
PyThreadState *_save;
int nStatus;
};
#endif // PYTHREADSTATELOCK_H
然后我们就可以在子线程调用python
//加载模块
void PythonHandler::PythonInsmod(QString pyfname)
{
class PyThreadStateLock PyThreadLock;//获取全局锁
// Import
if ( -1 == PyRun_SimpleString("import os") ) {
qCritical()<<"Python缺少OS库";
return ;
}
if ( -1 == PyRun_SimpleString("import math") ) {
qCritical()<<"Python缺少Math库";
return ;
}
if ( -1 == PyRun_SimpleString("import sys") ) {
qCritical()<<"Python缺少Sys库";
return ;
}
if ( -1 == PyRun_SimpleString("import time") ) {
qCritical()<<"Python缺少Time库";
return ;
}
if ( -1 == PyRun_SimpleString("from PIL import Image") ) {
qCritical()<<"Python缺少PIL库";
return ;
}
if ( -1 == PyRun_SimpleString("import tensorflow as tf") ) {
qCritical()<<"Python缺少Tensorflow库";
return ;
}
if ( -1 == PyRun_SimpleString("import numpy as np") ) {
qCritical()<<"Python缺少Numpy库";
return ;
}
if ( -1 == PyRun_SimpleString("import cv2") ) {
qCritical()<<"Python缺少OpenCV库";
return ;
}
// 导入py模块
m_module = PyImport_ImportModule(pyfname.toLocal8Bit().data());
if ( nullptr != m_module ) {
qDebug()<<"加载Python文件成功";
} else {
qCritical()<<"加载Python文件失败"<<pyfname;
return ;
}
// 载入方法
m_detection = PyObject_GetAttrString(m_module, "detection");
if ( nullptr != m_detection && PyCallable_Check( m_detection ) ) {
qDebug()<<"加载Python方法成功";
} else {
qCritical()<<"加载Python方法失败";
return ;
}
}
后续,就可以在子线程尽情地调用python函数了
本文参考:C++ 多线程调用Python脚本