Skip to content

Instantly share code, notes, and snippets.

@sterin
Last active November 7, 2022 12:28
Show Gist options
  • Save sterin/61561c3139dd49da1f43 to your computer and use it in GitHub Desktop.
Save sterin/61561c3139dd49da1f43 to your computer and use it in GitHub Desktop.
An example showing how to use sub interpreters and threads in Python
cmake_minimum_required(VERSION 2.8.4)
project(py1)
find_package(PythonLibs REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
ADD_DEFINITIONS( -std=c++11 )
set(SOURCE_FILES main.cpp)
add_executable(py1 ${SOURCE_FILES})
target_link_libraries(py1 ${PYTHON_LIBRARIES})
// Author: Baruch Sterin <baruchs@gmail.com>
#include <thread>
#include <Python.h>
// initialize and clean up python
struct initialize
{
initialize()
{
Py_InitializeEx(1);
PyEval_InitThreads();
}
~initialize()
{
Py_Finalize();
}
};
// allow other threads to run
class enable_threads
{
public:
enable_threads()
{
_state = PyEval_SaveThread();
}
~enable_threads()
{
PyEval_RestoreThread(_state);
}
private:
PyThreadState* _state;
};
// acquire and release the GIL
struct gil_lock
{
gil_lock()
{
PyEval_AcquireLock();
}
~gil_lock()
{
PyEval_ReleaseLock();
}
};
// restore the thread state when the object goes out of scope
class restore_tstate
{
public:
restore_tstate()
{
_ts = PyThreadState_Get();
}
~restore_tstate()
{
PyThreadState_Swap(_ts);
}
private:
PyThreadState* _ts;
};
// swap the current thread state with ts, restore when the object goes out of scope
class swap_tstate
{
public:
swap_tstate(PyThreadState* ts)
{
_ts = PyThreadState_Swap(ts);
}
~swap_tstate()
{
PyThreadState_Swap(_ts);
}
private:
PyThreadState* _ts;
};
// create new thread state for interpreter interp, clean up on destruction
class thread_state
{
public:
thread_state(PyInterpreterState* interp)
{
_ts = PyThreadState_New(interp);
}
~thread_state()
{
if( _ts )
{
PyThreadState_Clear(_ts);
PyThreadState_Delete(_ts);
_ts = nullptr;
}
}
operator PyThreadState*()
{
return _ts;
}
private:
PyThreadState* _ts;
};
// represent a sub interpreter
class sub_interpreter
{
public:
// perform the necessary setup and cleanup for a new thread running using a specific interpreter
struct thread
{
gil_lock _lock;
thread_state _state;
swap_tstate _swap;
thread(PyInterpreterState* interp) :
_state(interp),
_swap(_state)
{
}
};
sub_interpreter()
{
restore_tstate restore;
_ts = Py_NewInterpreter();
}
~sub_interpreter()
{
if( _ts )
{
swap_tstate sts(_ts);
Py_EndInterpreter(_ts);
}
}
PyInterpreterState* interp()
{
return _ts->interp;
}
private:
PyThreadState* _ts;
};
char program[] = "import sys; print \"ARGV: %s\"%hasattr(sys, \"argv\")";
char main_program[] = "import sys; sys.argv=sys.argv if hasattr(sys, \"argv\") else [\"abc\"] ; print \"ARGV: %d\"%id(sys.argv)";
// run in a new thread
void f(PyInterpreterState* interp)
{
sub_interpreter::thread scope(interp);
PyRun_SimpleString(program);
}
int main()
{
initialize init;
sub_interpreter s1;
sub_interpreter s2;
sub_interpreter s3;
std::thread t1(f, s1.interp() );
std::thread t2(f, s2.interp() );
std::thread t3(f, s3.interp() );
std::thread t4(f, s1.interp() );
PyRun_SimpleString(main_program);
enable_threads t;
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
@sven001
Copy link

sven001 commented Jun 13, 2019

The linked example works like a charm. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment