He escrito un módulo de extensión que usa punteros de funciones C++ para almacenar secuencias de llamadas a funciones. Quiero 'ejecutar' estas secuencias de llamadas en procesos separados utilizando el módulo multiprocessing
de python (no hay estado compartido, por lo que no hay problemas de sincronización).¿Los punteros de función siguen siendo válidos en todos los procesos?
Necesito saber si los punteros a las funciones (no los punteros de datos) siguen siendo válidos después de multiprocessing
si es fork()
.
C++ módulo:
#include <list>
#include <boost/assert.hpp>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/foreach.hpp>
/*
* Some functions to be called
*/
double funcA(double d) { return d; }
double funcB(double d) { return d + 3.14; }
double funcC(double d) { return d - 42.0; }
/*
* My container of function pointers (picklable to allow use with multiprocessing)
*/
typedef double(*func_ptr_t)(double);
struct CallSequence {
CallSequence() {
_seq.push_back(funcA);
_seq.push_back(funcB);
_seq.push_back(funcC);
}
std::list<func_ptr_t> _seq;
};
template <typename cast_type>
struct CallSequence_picklesuite : boost::python::pickle_suite {
BOOST_STATIC_ASSERT_MSG(sizeof(cast_type) == sizeof(func_ptr_t), CANNOT_CAST_POINTER_TO_REQUESTED_TYPE);
static boost::python::list getstate(const CallSequence& cs) {
boost::python::list ret;
BOOST_FOREACH(func_ptr_t p, cs._seq)
ret.append(reinterpret_cast<cast_type>(p));
return ret;
}
static void setstate(CallSequence& cs, boost::python::list l) {
std::list<func_ptr_t> new_list;
boost::python::stl_input_iterator<cast_type> begin(l), end;
for(; begin != end; begin++)
new_list.push_back(reinterpret_cast<func_ptr_t>(*begin));
cs._seq.swap(new_list);
}
};
/*
* Run the call sequence
*/
double runner(const CallSequence& cs) {
double ret = 0;
BOOST_FOREACH(const func_ptr_t& p, cs._seq)
ret += p(2.18);
return ret;
}
BOOST_PYTHON_MODULE(my_extension) {
using namespace ::boost::python;
class_<CallSequence>("CallSequence")
.def_pickle(CallSequence_picklesuite<unsigned int>());
def("runner", runner);
}
Compilado con:
$ g++ question1.cpp -lboost_python -I /usr/include/python2.7 -shared -o my_extension.so
código Python que la invoca a través de múltiples procesos:
#!/usr/bin/python
from multiprocessing import Pool
import my_extension
def runner(sequence):
return my_extension.runner(sequence)
def main():
l = [my_extension.CallSequence() for _ in range(200)]
pool = Pool(processes=4)
print pool.map(runner, l)
if __name__ == '__main__':
main()
la salida es como se esperaba. Quiero saber si estoy teniendo suerte o si puedo esperar que los punteros a la función sigan siendo válidos después de fork()
.
Con respecto a su comentario en mi respuesta (ahora eliminada), es porque no sabía 'fork' copia el espacio de direcciones. Mi respuesta habría sido correcta para dos procesos completamente independientes, que pensé que era 'fork', pero por supuesto estaba equivocado. +1 –
En Windows no se bifurca, pero ejecuta un nuevo intérprete de Python. Creo que esto también funcionará bien con el código del OP. Mira aquí: http://bugs.python.org/issue8713 –