6

我写了一个扩展模块,它使用C++函数指针来存储函数调用序列。我想使用python的multiprocessing模块在不同的进程中'运行'这些调用序列(没有共享状态,所以没有同步问题)。函数指针是否在进程间保持有效?

我需要知道函数指针(不是数据指针)在multiprocessing之后是否保持有效fork()

C++模块:

#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); 
} 

编译时:

$ g++ question1.cpp -lboost_python -I /usr/include/python2.7 -shared -o my_extension.so 

Python代码在多个流程调用它:

#!/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() 

如预期的输出。我想知道如果我只是'幸运',或者如果我能可靠地期望函数指针在fork()之后保持有效。

回答

4

当然 - 分叉时复制地址空间,所以指针对于父进程和子进程都是有效的。

+0

关于你对我的(现在删除的)答案的评论,这是因为我不知道'fork'复制地址空间。我的回答对于两个完全不相关的流程来说是正确的,我认为这是'叉'的作用,但我当然错了。 +1 –

+1

在Windows上它不分叉,但运行一个新的Python解释器。我认为这对OP的代码也可以正常工作。请参阅:http://bugs.python.org/issue8713 –