Question:  python 3.13.5 windows 10 msvc  This is how initialize python in main. #include <iostream> #include <taskflow/taskflow.hpp> // Taskflow is header-only #define PY_SSIZE_T_CLEAN #include <Python.h> int main(int argc, char* argv[]) { wchar_t pythonHome[] = L".venv"; Py_SetPythonHome(pythonHome); Py_Initialize(); PyEval_InitThreads(); if (!Py_IsInitialized()) { std::cerr << "Python failed to initialize\n"; return 1; } // Set up Python paths PyRun_SimpleString( "import sys\n" "sys.path.insert(0, '.venv/Lib')\n" "sys.path.insert(0, '.venv/Lib/site-packages')\n" ); // Test script execution PyRun_SimpleString( "from time import time, ctime\n" "print('Today is', ctime(time()))\n" ); PyObject* main = PyImport_AddModule("__main__"); PyObject* globals = PyModule_GetDict(main); tf::Executor executor; tf::Taskflow taskflow; auto [A, B, C, D] = taskflow.emplace( // create four tasks [] () { std::cout << "TaskA\n"; PyGILState_STATE gstate = PyGILState_Ensure();PyGILState_Release(gstate);}, [] () { std::cout << "TaskB\n"; }, [] () { std::cout << "TaskC\n"; }, [] () { std::cout << "TaskD\n"; } ); A.precede(B, C); // A runs before B and C D.succeed(B, C); // D runs after B and C executor.run(taskflow).wait(); if (Py_FinalizeEx() < 0) { return 120; } return 0; }  Python is getting properly initialized. It hangs at the execution of TaskA. You can't even close the app by pressing Crtl+C. This is pretty much explains my requirement to execute the python code inside tasks.

Answer: that's because each time you acquire the gil you must release it, the main thread starts by acquiring the GIL once so you must reset the initial GIL count in the main thread by calling PyEval_SaveThread in the main thread. Py_InitializeEx(); PyThreadState* tstate = PyEval_SaveThread(); // release the initial GIL // before running python code auto gil = PyGILState_Ensure(); // ... running Python code PyGILState_Release(gil);  It should look as follows in your code #include <iostream> #include <taskflow/taskflow.hpp> // Taskflow is header-only #define PY_SSIZE_T_CLEAN #include <Python.h> int main(int argc, char* argv[]) { wchar_t pythonHome[] = L".venv"; Py_SetPythonHome(pythonHome); Py_Initialize(); PyEval_InitThreads(); // empty body since python 3.9 if (!Py_IsInitialized()) { std::cerr << "Python failed to initialize\n"; return 1; } // Set up Python paths PyRun_SimpleString( "import sys\n" "sys.path.insert(0, '.venv/Lib')\n" "sys.path.insert(0, '.venv/Lib/site-packages')\n" ); // Test script execution PyRun_SimpleString( "from time import time, ctime\n" "print('Today is', ctime(time()))\n" ); PyObject* main = PyImport_AddModule("__main__"); PyObject* globals = PyModule_GetDict(main); PyThreadState* tstate = PyEval_SaveThread(); // release the initial GIL tf::Executor executor; tf::Taskflow taskflow; auto [A, B, C, D] = taskflow.emplace( // create four tasks [] () { std::cout << "TaskA\n"; PyGILState_STATE gstate = PyGILState_Ensure();PyGILState_Release(gstate);}, [] () { std::cout << "TaskB\n"; }, [] () { std::cout << "TaskC\n"; }, [] () { std::cout << "TaskD\n"; } ); A.precede(B, C); // A runs before B and C D.succeed(B, C); // D runs after B and C executor.run(taskflow).wait(); PyEval_RestoreThread(tstate); // restore the GIL before calling finalize if (Py_FinalizeEx() < 0) { return 120; } return 0; }  there's also Py_BEGIN_ALLOW_THREADS which does the same thing if you are sure that you will eventually re-acquire the gil on the main thread (which just calls PyEval_SaveThread under the hood with a scope block)