2016-12-14 11 views
2

저는 C++ 모듈 (.so, boost.python 사용)이 포함 된 Python 프로그램을 작성하고 있습니다.
C++ 함수를 실행하는 여러 파이썬 스레드를 시작합니다.boost.python C++ multithreading

#include <boost/python.hpp> 
using namespace boost; 
void f(){ 
    // long calculation 

    // call python function 

    // long calculation 
} 

BOOST_PYTHON_MODULE(test) 
{ 
    python::def("f", &f); 
} 

그리고 파이썬 코드 : 나는 문제가 발생할

from test import f 
t1 = threading.Thread(target=f) 
t1.setDaemon(True) 
t1.start() 
print "Still running!" 

다음 C++ 코드가 어떻게 보이는지

입니다! "아직 실행의" 메시지가 표시되지 않고 C++ 스레드가 GIL을 보유하고 있음을 알았습니다.

파이썬 코드에서 C++ 코드를 실행 한 경우 GIL을 처리하는 가장 좋은 방법은 무엇입니까?

감사합니다. Gal

+0

내가 :: 부스트를 알고 파이썬 (그러나 언급에 감사하지 않습니다 이름은 매우 흥미 롭습니다.) 그러나이 답변은 질문을 해결할 수있는 것처럼 보입니다 : ['struct no_gil'] (http://stackoverflow.com/a/18648366/416224). – kay

+0

감사합니다. 그 saveThread/restoreThread 메소드를 보았지만 파이썬 코드를 호출하기 위해 함수 내에서 Gil을 다시 가져와야합니다. –

답변

1

종종 RAII-style 클래스를 사용하여 Global Interpreter Lock (GIL)을 관리하는 클래스는 우아한 예외 안전 솔루션을 제공합니다.

예를 들어 with_gil 클래스를 사용하면 with_gil 개체를 만들 때 호출 스레드가 GIL을 가져옵니다. with_gil 개체가 소멸되면 GIL 상태로 복원됩니다.

/// @brief Guard that will acquire the GIL upon construction, and 
///  restore its state upon destruction. 
class with_gil 
{ 
public: 
    with_gil() { state_ = PyGILState_Ensure(); } 
    ~with_gil() { PyGILState_Release(state_); } 

    with_gil(const with_gil&)   = delete; 
    with_gil& operator=(const with_gil&) = delete; 
private: 
    PyGILState_STATE state_; 
}; 

그리고 보완 without_gil 클래스는 반대를 수행합니다

/// @brief Guard that will unlock the GIL upon construction, and 
///  restore its staet upon destruction. 
class without_gil 
{ 
public: 
    without_gil() { state_ = PyEval_SaveThread(); } 
    ~without_gil() { PyEval_RestoreThread(state_); } 

    without_gil(const without_gil&)   = delete; 
    without_gil& operator=(const without_gil&) = delete; 
private: 
    PyThreadState* state_; 
}; 

다음과 같이 함수 내에서 사용이 될 수있다 :

void f() 
{ 
    without_gil no_gil;  // release gil 
    // long calculation 
    ... 

    { 
    with_gil gil;   // acquire gil 
    // call python function 
    ... 
    }       // restore gil (release) 

    // long calculation 
    ... 
}       // restore gil (acquire) 

하나에도 높은 수준의 편리한 클래스를 사용할 수 있습니다 std::lock_guard과 같은 경험을 제공하십시오. GIL 획득 및 해제, 저장 및 복원 의미는 일반적인 뮤텍스와 약간 다릅니다.따라서 gil_guard 인터페이스는 다른 :

  • gil_guard.acquire() GIL로
  • gil_guard.release()을 취득합니다 이전 상태
  • /// @brief Guard that provides higher-level GIL controls. 
    class gil_guard 
    { 
    public: 
        struct no_acquire_t {} // tag type used for gil acquire strategy 
        static no_acquire; 
    
        gil_guard()    { acquire(); } 
        gil_guard(no_acquire_t) { release(); } 
        ~gil_guard()   { while (!stack_.empty()) { restore(); } } 
    
        void acquire()   { stack_.emplace(new with_gil); } 
        void release()   { stack_.emplace(new without_gil); } 
        void restore()   { stack_.pop(); } 
    
        static bool owns_gil() 
        { 
        // For Python 3.4+, one can use `PyGILState_Check()`. 
        return _PyThreadState_Current == PyGILState_GetThisThreadState(); 
        } 
    
        gil_guard(const gil_guard&)   = delete; 
        gil_guard& operator=(const gil_guard&) = delete; 
    
    private: 
        // Use std::shared_ptr<void> for type erasure. 
        std::stack<std::shared_ptr<void>> stack_; 
    }; 
    

그리고 그것의 사용이 될 것를 복원 할 GIL
  • gil_guard_restore()을 발표 할 예정이다 :

    여기

    (10)는 이러한 보조 클래스와 완벽한 예를 demonstrating GIL 관리입니다 :

    #include <cassert> 
    #include <iostream> // std::cout, std::endl 
    #include <memory> // std::shared_ptr 
    #include <thread> // std::this_thread 
    #include <stack> // std::stack 
    #include <boost/python.hpp> 
    
    /// @brief Guard that will acquire the GIL upon construction, and 
    ///  restore its state upon destruction. 
    class with_gil 
    { 
    public: 
        with_gil() { state_ = PyGILState_Ensure(); } 
        ~with_gil() { PyGILState_Release(state_); } 
    
        with_gil(const with_gil&)   = delete; 
        with_gil& operator=(const with_gil&) = delete; 
    private: 
        PyGILState_STATE state_; 
    }; 
    
    /// @brief Guard that will unlock the GIL upon construction, and 
    ///  restore its staet upon destruction. 
    class without_gil 
    { 
    public: 
        without_gil() { state_ = PyEval_SaveThread(); } 
        ~without_gil() { PyEval_RestoreThread(state_); } 
    
        without_gil(const without_gil&)   = delete; 
        without_gil& operator=(const without_gil&) = delete; 
    private: 
        PyThreadState* state_; 
    }; 
    
    /// @brief Guard that provides higher-level GIL controls. 
    class gil_guard 
    { 
    public: 
        struct no_acquire_t {} // tag type used for gil acquire strategy 
        static no_acquire; 
    
        gil_guard()    { acquire(); } 
        gil_guard(no_acquire_t) { release(); } 
        ~gil_guard()   { while (!stack_.empty()) { restore(); } } 
    
        void acquire()   { stack_.emplace(new with_gil); } 
        void release()   { stack_.emplace(new without_gil); } 
        void restore()   { stack_.pop(); } 
    
        static bool owns_gil() 
        { 
        // For Python 3.4+, one can use `PyGILState_Check()`. 
        return _PyThreadState_Current == PyGILState_GetThisThreadState(); 
        } 
    
        gil_guard(const gil_guard&)   = delete; 
        gil_guard& operator=(const gil_guard&) = delete; 
    
    private: 
        // Use std::shared_ptr<void> for type erasure. 
        std::stack<std::shared_ptr<void>> stack_; 
    }; 
    
    void f() 
    { 
        std::cout << "in f()" << std::endl; 
    
        // long calculation 
        gil_guard gil(gil_guard::no_acquire); 
        assert(!gil.owns_gil()); 
        std::this_thread::sleep_for(std::chrono::milliseconds(500)); 
        std::cout << "calculating without gil..." << std::endl; 
    
        // call python function 
        gil.acquire(); 
        assert(gil.owns_gil()); 
        namespace python = boost::python; 
        python::object print = 
        python::import("__main__").attr("__builtins__").attr("print"); 
        print(python::str("calling a python function")); 
        gil.restore(); 
    
        // long calculation 
        assert(!gil.owns_gil()); 
        std::cout << "calculating without gil..." << std::endl; 
    } 
    
    BOOST_PYTHON_MODULE(example) 
    { 
        // Force the GIL to be created and initialized. The current caller will 
        // own the GIL. 
        PyEval_InitThreads(); 
    
        namespace python = boost::python; 
        python::def("f", +[] { 
        // For exposition, assert caller owns GIL before and after 
        // invoking function `f()`. 
        assert(gil_guard::owns_gil()); 
        f(); 
        assert(gil_guard::owns_gil()); 
        }); 
    } 
    

    대화 형 사용 :

    >>> import threading 
    >>> import example 
    >>> t1 = threading.Thread(target=example.f) 
    >>> t1.start(); print "Still running" 
    in f() 
    Still running 
    calculating without gil... 
    calling a python function 
    calculating without gil... 
    >>> t1.join() 
    
  • +0

    'std :: shared_ptr '에 대해 확실합니까? 내가 잘못 본 것이 아니라면, 정확한 토르가 호출되지 않을 것입니다. 'boost :: variant '이 더 쉽게 이해할 수있는 해결책이 될 수 있을까요? – kay

    +1

    @Kay'std :: shared_ptr '이 적절한 소멸자를 호출합니다. 생성자는 또한 'std :: shared_ptr :: shared_ptr (Y * p)'형식의 템플릿입니다. 이 표준은'p'가'T *'로 변환 가능하고'delete p' 표현식이 잘 형성되어야한다고 요구합니다. 나는'boost :: variant'가 한눈에 이해하기 쉽다는 것에 동의합니다. 그러나 유형 삭제를 수행하면 누군가가 요소를 조작하지 못하게됩니다. –