A correct order of steps to perform what you are trying to do is:
-
In the main thread:
- Initialize Python using
Py_Initialize*
. - Initialize Python threading support using
PyEval_InitThreads()
. - Start the C++ thread.
- Initialize Python using
At this point, the main thread still holds the GIL.
- In a C++ thread:
- Acquire the GIL using
PyGILState_Ensure()
. - Create a new Python thread object and start it.
- Release the GIL using
PyGILState_Release()
. - Sleep, do something useful or exit the thread.
- Acquire the GIL using
Because the main thread holds the GIL, this thread will be waiting to acquire the GIL. If the main thread calls the Python API it may release the GIL from time to time allowing the Python thread to execute for a little while.
- Back in the main thread:
- Release the GIL, enabling threads to run using
PyEval_SaveThread()
- Before attempting to use other Python calls, reacquire the GIL using
PyEval_RestoreThread()
- Release the GIL, enabling threads to run using
I suspect that you are missing the last step – releasing the GIL in the main thread, allowing the Python thread to execute.
I have a small but complete example that does exactly that at this link.