Skip to content

Instantly share code, notes, and snippets.

@asvetlov
Created January 31, 2013 04:19
Show Gist options
  • Save asvetlov/4680136 to your computer and use it in GitHub Desktop.
Save asvetlov/4680136 to your computer and use it in GitHub Desktop.
// 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