Incorporando um interpretador Python em um programa C ++ multithread com pybind11
Estou tentando usar pybind11 para fazer uma biblioteca C ++ de terceiros chamar um método Python. A biblioteca é multithread e cada thread cria um objeto Python e, a seguir, faz várias chamadas aos métodos do objeto.
Meu problema é que a chamada para py::gil_scoped_acquire acquire;
deadlocks. Um código mínimo que reproduz o problema é fornecido abaixo. O que estou fazendo de errado?
// main.cpp
class Wrapper
{
public:
Wrapper()
{
py::gil_scoped_acquire acquire;
auto obj = py::module::import("main").attr("PythonClass")();
_get_x = obj.attr("get_x");
_set_x = obj.attr("set_x");
}
int get_x()
{
py::gil_scoped_acquire acquire;
return _get_x().cast<int>();
}
void set_x(int x)
{
py::gil_scoped_acquire acquire;
_set_x(x);
}
private:
py::object _get_x;
py::object _set_x;
};
void thread_func()
{
Wrapper w;
for (int i = 0; i < 10; i++)
{
w.set_x(i);
std::cout << "thread: " << std::this_thread::get_id() << " w.get_x(): " << w.get_x() << std::endl;
std::this_thread::sleep_for(100ms);
}
}
int main() {
py::scoped_interpreter python;
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i)
threads.push_back(std::thread(thread_func));
for (auto& t : threads)
t.join();
return 0;
}
e o código Python:
// main.py
class PythonClass:
def __init__(self):
self._x = 0
def get_x(self):
return self._x
def set_x(self, x):
self._x = x
Perguntas relacionadas podem ser encontradas aqui e aqui , mas não me ajudaram a resolver o problema.
Respostas
Consegui resolver o problema liberando o GIL no thread principal, antes de iniciar os threads de trabalho (adicionados py::gil_scoped_release release;
). Para qualquer pessoa interessada, agora funciona o seguinte (também adicionado a limpeza de objetos Python):
#include <pybind11/embed.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <sstream>
namespace py = pybind11;
using namespace std::chrono_literals;
class Wrapper
{
public:
Wrapper()
{
py::gil_scoped_acquire acquire;
_obj = py::module::import("main").attr("PythonClass")();
_get_x = _obj.attr("get_x");
_set_x = _obj.attr("set_x");
}
~Wrapper()
{
_get_x.release();
_set_x.release();
}
int get_x()
{
py::gil_scoped_acquire acquire;
return _get_x().cast<int>();
}
void set_x(int x)
{
py::gil_scoped_acquire acquire;
_set_x(x);
}
private:
py::object _obj;
py::object _get_x;
py::object _set_x;
};
void thread_func(int iteration)
{
Wrapper w;
for (int i = 0; i < 10; i++)
{
w.set_x(i);
std::stringstream msg;
msg << "iteration: " << iteration << " thread: " << std::this_thread::get_id() << " w.get_x(): " << w.get_x() << std::endl;
std::cout << msg.str();
std::this_thread::sleep_for(100ms);
}
}
int main() {
py::scoped_interpreter python;
py::gil_scoped_release release; // add this to release the GIL
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i)
threads.push_back(std::thread(thread_func, 1));
for (auto& t : threads)
t.join();
return 0;
}
Python é conhecido por ter um bloqueio global de intérprete .
Basicamente, você precisa escrever seu próprio interpretador Python do zero ou baixar o código-fonte do Python e melhorá-lo muito.
Se você estiver no Linux, pode considerar a execução de muitos interpretadores Python (usando syscalls (2) apropriados , com pipe (7) ou unix (7) para comunicação entre processos) - talvez um processo Python se comunicando com cada um de seus threads C ++.
O que estou fazendo de errado?
Codificar em Python algo que deveria ser codificado de outra forma. Você considerou experimentar o SBCL ?
Algumas bibliotecas (por exemplo, Tensorflow ) podem ser chamadas de Python e C ++. Talvez você possa se inspirar neles ...
Na prática, se você tiver apenas uma dúzia de threads C ++ em uma máquina Linux poderosa, poderá ter um processo Python por thread C ++. Portanto, cada thread C ++ teria seu próprio processo Python associado.
Caso contrário, planeje vários anos de trabalho para melhorar o código-fonte do Python para remover seu GIL. Você pode codificar seu plugin GCC para ajudá-lo nessa tarefa - analisar e compreender o código C do Python.