Created
January 31, 2013 04:19
-
-
Save asvetlov/4680136 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// pymulti.cpp — основной код | |
#include <iostream> | |
#include <cstdlib> | |
#include <conio.h> | |
#include <boost/python.hpp> | |
#include <boost/thread.hpp> | |
using namespace std; | |
using namespace boost::python; | |
const int THREAD_NUM = 10; // количество рабочих потоков | |
const int REPEAT_TIMES = 5; // количество повторения тестов | |
const double PAUSE_SEC = 0.5; // значение паузы в секундах | |
class PyMainThread // специальный класс для основного потока | |
{ | |
public: | |
PyMainThread() // нужно создать экземпляр класса в самом начале работы | |
{ | |
Py_Initialize(); // инициализация интерпретатора Python | |
PyEval_InitThreads(); // инициализация потоков в Python и механизма GIL | |
mGilState = PyGILState_Ensure(); // забираем себе GIL сразу для настройки многопоточности | |
mThreadState = PyEval_SaveThread(); // сохраняем состояние главного потока и отпускаем GIL | |
// здесь GIL разблокирован для основного интерпретатора Python и ждёт блокировки из других потоков | |
} | |
~PyMainThread() // по завершении работы нужно корректно освободит ресурсы интерпретатора | |
{ | |
// здесь GIL должен быть разблокирован для основного интерпретатора | |
PyEval_RestoreThread( mThreadState ); // восстанавливаем состояние главного потока и забираем себе GIL | |
PyGILState_Release( mGilState ); // отпускаем блокировку GIL с сохранённым состоянием | |
Py_Finalize(); // завершает работу как основного интерпретатора, со всеми под-интерпретаторами Python | |
} | |
private: | |
PyGILState_STATE mGilState; // сохранённое состояние GIL | |
PyThreadState* mThreadState; // сохранённое состояние основного потока | |
}; | |
class PySubThread // класс для работы в каждом порождённом потоке | |
{ | |
public: | |
PySubThread() // нужно для инициализации под-интерпретатора Python в самом начале работы потока | |
{ | |
mMainGilState = PyGILState_Ensure(); // забираем блокировку основного интерпретатора | |
mOldThreadState = PyThreadState_Get(); // сохраняем текущее состояние порождённого потока | |
mNewThreadState = Py_NewInterpreter(); // создаём в потоке под-интерпретатор | |
PyThreadState_Swap( mNewThreadState ); // с этого момента для потока актуален уже под-интерпретатор | |
mSubThreadState = PyEval_SaveThread(); // не забываем освободить предыдущую блокировку GIL | |
mSubGilState = PyGILState_Ensure(); // и заблокировать GIL уже для под-интерпретатора | |
} | |
~PySubThread() // по завершении работы потока нужно корректно освободить ресурсы под-интепретатора Python | |
{ | |
PyGILState_Release( mSubGilState ); // разблокируем GIL для под-интерпретатора | |
PyEval_RestoreThread( mSubThreadState ); // восстанавливаем блокировку и состояние потока для основного интерпретатора | |
Py_EndInterpreter( mNewThreadState ); // завершаем работу под-интерпретатора | |
PyThreadState_Swap( mOldThreadState ); // возвращаем состояние потока для основного интерпретатора | |
PyGILState_Release( mMainGilState ); // освобождаем блокировку GIL для основного интерпретатора | |
} | |
private: | |
PyGILState_STATE mMainGilState; // состояние GIL основного интерпретатора Python | |
PyThreadState* mOldThreadState; // состояние текущего потока для основного интерпретатора | |
PyThreadState* mNewThreadState; // состояние потока для порождённого под-интерпретатора | |
PyThreadState* mSubThreadState; // сохранённое состояние потока при разблокировке GIL | |
PyGILState_STATE mSubGilState; // состояние GIL для порождённого под-интерпретатора Python | |
}; | |
class ThreadWork // тестовый класс-функтор для передачи в экземпляр boost::thread | |
{ | |
public: | |
ThreadWork( int id ) // сохраним порядковый номер запущенного потока | |
: mID( id ) | |
{ | |
} | |
void operator () ( void ) // собственно выполняемая работа в каждом потоке | |
{ | |
cout << "Thread#" << mID << " <= START" << endl; | |
PySubThread sub_thread; // здесь мы порождаем под-интерпретатор Python | |
for( int rep = 1; rep <= REPEAT_TIMES; ++rep ) | |
{ | |
// работаем с модулем написаном на Python | |
cout << "Thread#" << mID << " <= Repeat#" << rep << " <= import time; time.sleep(pause)" << endl; | |
object time = import( "time" ); // import time | |
time.attr( "sleep" )( PAUSE_SEC ); // time.sleep(pause) | |
// работаем с модулем написаном на C++ | |
cout << "Thread#" << mID << " <= Repeat#" << rep << " <= import ctimer; ctimer.wait(pause)" << endl; | |
object ctimer = import( "ctimer" ); // import ctimer | |
ctimer.attr( "wait" )( PAUSE_SEC ); // ctimer.wait(pause) | |
} | |
cout << "Thread#" << mID << " <= END" << endl; | |
// здесь под-интерпретатор Python завершит свою работу | |
} | |
private: | |
int mID; // порядковый номер при запуске потоков | |
}; | |
void waitkey() // ожидание нажатия клавиши по завершении | |
{ | |
cout << "\nPress any key... "; | |
_getch(); | |
} | |
int main() // точка входа, если кто забыл | |
{ | |
cout << "Multi-thread Python with GIL" << endl; | |
atexit(waitkey); // по завершении работы вызовется функция ожидания нажатия клавиши | |
PyMainThread main_thread; // начальная инициализация интерпретатора в главном потоке | |
boost::thread_group group; | |
// порождаем потоки, каждый выполняет свою работу, взаимодействуя с Python без общего GIL | |
for( int id = 1; id <= THREAD_NUM; ++id) | |
group.create_thread( ThreadWork(id) ); | |
group.join_all(); | |
return 0; | |
// в конце работы в деструкторе PyMainThread всё должно корректно де-инициализироваться | |
} | |
//////////////////////////////////////////////////////////////// | |
// ctimer.cpp — реализация модуля ctimer с функцией wait используемой из pymulti | |
#include <boost/python.hpp> | |
#include <boost/thread.hpp> | |
using namespace boost::python; | |
using namespace boost::this_thread; | |
using namespace boost::posix_time; | |
void wait( double sec ) // функция ожидания, аналог стандарному time.sleep в Python | |
{ | |
int msec = static_cast<int>( sec * 1000 ); // переводим в миллисекунды | |
sleep( millisec( msec ) ); // спим указанный в секундах период | |
} | |
BOOST_PYTHON_MODULE( ctimer ) // используем boost::python и создаём модуль ctimer | |
{ | |
def( "wait", wait, args("sec") ); // в Python это будет ctimer.wait(sec) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment