I have the following C++ code that launches 4 threads. Each thread calls the Py_NewInterpreterFromConfig
and runs a python script named gettid.py
:
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <string>
#include <filesystem>
#include "unistd.h"
#include "Python.h"
void execute_python_code(
PyInterpreterConfig config, const std::string& module_name, const std::string& func_name, std::mutex& print_mutex) {
const auto currentDir = std::filesystem::current_path();
// PyGILState_STATE gstate;
// interpreter 1
PyThreadState *tstate1 = NULL;
// gstate = PyGILState_Ensure();
PyStatus status1 = Py_NewInterpreterFromConfig(&tstate1, &config);
if (PyStatus_Exception(status1)) {
Py_ExitStatusException(status1);
std::cout << "Failed\n";
return;
}
PyThreadState_Swap(tstate1);
PyObject *sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString(currentDir.c_str()));
PyObject* myModule = PyImport_ImportModule(module_name.c_str());
PyErr_Print();
PyObject* myFunction = PyObject_GetAttrString(myModule, func_name.c_str());
PyObject* res = PyObject_CallObject(myFunction, NULL);
// PyGILState_Release(gstate);
if (res) {
const int x = (int)PyLong_AsLong(res);
{
std::lock_guard<std::mutex> guard(print_mutex);
std::cerr << "C++ thread id = " << gettid() << ", python result = " << x << std::endl;
}
}
// destroy interpreter 1
PyThreadState_Swap(tstate1);
Py_EndInterpreter(tstate1);
}
int main(int argc, char* argv[]) {
std::mutex print_mutex;
std::string pymodule;
std::string pyfuncname;
if (argc == 3) {
pyfuncname = std::string(argv[2]);
argc--;
} else {
pyfuncname = "gettid";
}
if (argc == 2) {
pymodule = std::string(argv[1]);
argc--;
} else {
pymodule = "gettid";
}
Py_InitializeEx(0);
PyInterpreterConfig config = {
.use_main_obmalloc = 0,
.allow_fork = 1,
.allow_exec = 1,
.allow_threads = 1,
.allow_daemon_threads = 0,
.check_multi_interp_extensions = 1,
.gil = PyInterpreterConfig_OWN_GIL,
};
const int num_threads = 4;
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back(
execute_python_code, config, pymodule, pyfuncname, std::ref(print_mutex));
}
for (int i = 0; i < num_threads; ++i) {
threads[i].join();
}
int status_exit = Py_FinalizeEx();
std::cout << "status_exit: " << status_exit << std::endl;
return 0;
}
The python script gettid.py
simply prints the thread ID:
#!/usr/bin/env python3
import threading
def gettid():
return threading.get_native_id()
With python 3.12, I can compile the code and run it successfully:
C++ thread id = 37174, python result = 37174
C++ thread id = 37175, python result = 37175
C++ thread id = 37177, python result = 37177
C++ thread id = 37176, python result = 37176
status_exit: 0
With python 3.13, the C++ code seems stuck at Py_NewInterpreterFromConfig
. Any ideas?