python3中的PyEval_InitThreads:如何/何时调用它?(故事继续,令人恶心)

python3中的PyEval_InitThreads:如何/何时调用它?(故事继续,令人恶心),python,python-3.x,python-c-api,python-3.2,python-c-extension,Python,Python 3.x,Python C Api,Python 3.2,Python C Extension,基本上,对于什么时候应该调用以及需要什么样的API调用,似乎存在着大量的混淆/歧义。不幸的是,这个问题非常模糊。关于这个话题已经有很多,事实上,我个人已经讨论过这个话题,所以如果这是一个重复的话题,我不会特别惊讶;但是考虑到这个问题似乎没有明确的答案。(遗憾的是,我没有Guido Van Rossum的快速拨号。) 首先,让我们在这里定义问题的范围:我想做什么?好吧。。。我想用C编写一个Python扩展模块,它将: 使用C中的pthreadAPI生成工作线程 从这些C线程中调用Python回调

基本上,对于什么时候应该调用以及需要什么样的API调用,似乎存在着大量的混淆/歧义。不幸的是,这个问题非常模糊。关于这个话题已经有很多,事实上,我个人已经讨论过这个话题,所以如果这是一个重复的话题,我不会特别惊讶;但是考虑到这个问题似乎没有明确的答案。(遗憾的是,我没有Guido Van Rossum的快速拨号。)

首先,让我们在这里定义问题的范围:我想做什么?好吧。。。我想用C编写一个Python扩展模块,它将:

  • 使用C中的
    pthread
    API生成工作线程
  • 从这些C线程中调用Python回调
  • 好的,让我们从Python文档本身开始。他们说:

    void PyEval_InitThreads()

    初始化并获取全局解释器锁。应该是 在创建第二个线程或啮合之前在主线程中调用 在任何其他线程操作中,如PyEval_ReleaseThread(tstate)。 在调用PyEval_SaveThread()或 PyEval_RestoreThread()

    所以我的理解是:

  • 任何产生线程的C扩展模块都必须调用
    PyEval_InitThreads()
    在任何其他线程之前从主线程开始 产卵
  • 调用
    PyEval\u InitThreads
    锁定GIL
  • 所以常识告诉我们,任何创建线程的C扩展模块都必须调用
    PyEval_InitThreads()
    ,然后释放全局解释器锁。好吧,看起来很简单。因此,从表面上看,所需的只是以下代码:

    PyEval_InitThreads(); /* initialize threading and acquire GIL */
    PyEval_ReleaseLock(); /* Release GIL */
    
    看起来很容易。。。但不幸的是,Python 3.2文档也这么说。相反,为了释放GIL,我们应该使用:

    PyThreadState*PyEval\u SaveThread()

    释放全局解释器锁(如果已创建并执行线程 并将线程状态重置为NULL,返回 上一个线程状态(不为NULL)。如果锁已经锁好了 创建时,当前线程必须已获取它

    呃。。。好的,我想C扩展模块需要说:

    PyEval_InitThreads();
    PyThreadState* st = PyEval_SaveThread();
    

    事实上,这正是我们所说的。除非我在实践中实际尝试了这一点,否则在导入扩展模块时Python解释器会立即seg错误。美好的


    好了,现在我放弃了官方的Python文档,转而使用Google。因此,声明您需要从扩展模块执行的所有操作就是调用
    PyEval\u InitThreads()
    。当然,文档声称
    PyEval_InitThreads()
    获取了GIL,实际上,a揭示了它确实调用了内部函数
    take_GIL(PyThreadState_GET())

    因此
    PyEval_InitThreads()
    肯定获得了GIL。我认为,在调用
    PyEval\u InitThreads()
    之后,您绝对需要以某种方式释放GIL但是怎么做呢?
    PyEval\u ReleaseLock()
    已被弃用,而
    PyEval\u SaveThread()
    只是莫名其妙地隔离了故障

    好的。。。因此,也许出于某种目前我无法理解的原因,C扩展模块不需要释放GIL。我试过了。。。而且,正如预期的那样,一旦另一个线程尝试获取GIL(使用),程序就会从死锁中挂起。所以是的。。。调用
    PyEval\u InitThreads()
    后,确实需要释放GIL

    同样,问题是:打电话后如何释放GIL?


    更一般地说:为了能够从辅助C线程安全地调用Python代码,C扩展模块必须做什么?

    您不需要在扩展模块中调用它。这是为了初始化解释器,如果正在导入C-API扩展模块,那么解释器已经完成了初始化。此接口将用于嵌入应用程序


    您的理解是正确的:调用
    PyEval\u InitThreads
    可以获得GIL。在正确编写的Python/C应用程序中,这不是问题,因为GIL将及时自动或手动解锁

    如果主线程继续运行Python代码,则没有什么特别的事情要做,因为Python解释器将在执行多条指令后自动放弃GIL(允许另一个线程获取GIL,这将再次放弃GIL,依此类推)。此外,每当Python要调用阻塞系统调用时(例如从网络读取或写入文件),它都会在调用前后释放GIL

    这个答案的原始版本基本上到此为止。但还有一件事需要考虑:嵌入场景

    在嵌入Python时,主线程通常初始化Python并继续执行其他与Python无关的任务。在这种情况下,不会自动释放GIL,因此这必须由线程本身完成。这绝不是特定于调用
    PyEval_InitThreads
    的调用,它应该在获取GIL的情况下调用

    例如,
    main()
    可能包含如下代码:

    Py_Initialize();
    PyEval_InitThreads();
    
    Py_BEGIN_ALLOW_THREADS
    ... call the non-Python part of the application here ...
    Py_END_ALLOW_THREADS
    
    Py_Finalize();
    
    如果您的代码手动创建线程,那么他们需要在执行任何与Python相关的操作之前获取GIL,即使是像
    Py\u INCREF
    这样简单的操作。为此,请使用:


    调用PyEval_SaveThread的建议是有效的

    PyEval_InitThreads();
    PyThreadState* st = PyEval_SaveThread();
    
    然而,为了防止模块导入时崩溃,请确保要导入的Python API使用

    PyGILState_确保和PyGILState_发布

    e、 g


    我见过与你类似的症状:
    PyEval_InitThreads();
    PyThreadState* st = PyEval_SaveThread();
    
    PyGILState_STATE gstate = PyGILState_Ensure();
    PyObject *pyModule_p = PyImport_Import(pyModuleName_p);
    PyGILState_Release(gstate);
    
      Py_InitializeEx(0);
      if (!PyEval_ThreadsInitialized()) {
        PyEval_InitThreads();
        PyThreadState* mainPyThread = PyEval_SaveThread();
      }
    
      PyGILState_STATE gstate;
      gstate = PyGILState_Ensure();
      // Python C API calls
      PyGILState_Release(gstate);
    
    if (!PyEval_ThreadsInitialized())
    {
        PyEval_InitThreads();
    }
    
    // Do only non-Python things up to this point
    PyGILState_STATE state = PyGILState_Ensure();
    // Do Python-things here, like PyRun_SimpleString(...)
    PyGILState_Release(state);
    // ... and now back to doing only non-Python things
    
    Py_InitializeEx(0);
        if (!PyEval_ThreadsInitialized()) {
        PyEval_InitThreads();
        PyThreadState* mainPyThread = PyEval_SaveThread();
    }
    
    if (!PyEval_ThreadsInitialized()){
        PyEval_InitThreads();
    }
    //other codes
    while(alive) {
        Py_BEGIN_ALLOW_THREADS
        sleep or other block code
        Py_END_ALLOW_THREADS
    }
    
    main(){     
    //initialize Python
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
        "print 'In Main, Today is',ctime(time())\n");
    
    //to Initialize and acquire the global interpreter lock
    PyEval_InitThreads();
    
    //release the lock  
    PyThreadState *_save;
    _save = PyEval_SaveThread();
    
    // Create threads.
    for (int i = 0; i<MAX_THREADS; i++)
    {   
        hThreadArray[i] = CreateThread
        //(...
            MyThreadFunction,       // thread function name
        //...)
    
    } // End of main thread creation loop.
    
    // Wait until all threads have terminated.
    //...
    //Close all thread handles and free memory allocations.
    //...
    
    //end python here
    //but need to check for GIL here too
    PyEval_RestoreThread(_save);
    Py_Finalize();
    return 0;
    }
    
    //the thread function
    
    DWORD WINAPI MyThreadFunction(LPVOID lpParam)
    {
    //non Pythonic activity
    //...
    
    //check for the state of Python GIL
    PyGILState_STATE gilState;
    gilState = PyGILState_Ensure();
    //execute Python here
    PyRun_SimpleString("from time import time,ctime\n"
        "print 'In Thread Today is',ctime(time())\n");
    //release the GIL           
    PyGILState_Release(gilState);   
    
    //other non Pythonic activity
    //...
    return 0;
    }
    
    int main()
    {
    
    // Initialize the main interpreter
    Py_Initialize();
    // Initialize and acquire the global interpreter lock
    PyEval_InitThreads();
    // Release the lock     
    PyThreadState *_save;
    _save = PyEval_SaveThread();
    
    
    // create threads
    for (int i = 0; i<MAX_THREADS; i++)
    {
    
        // Create the thread to begin execution on its own.
    
        hThreadArray[i] = CreateThread
        //(...
    
            MyThreadFunction,       // thread function name
        //...);   // returns the thread identifier 
    
    } // End of main thread creation loop.
    
      // Wait until all threads have terminated.
    WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
    
    // Close all thread handles and free memory allocations.
    // ...
    
    
    //end python here
    //but need to check for GIL here too
    //re capture the lock
    PyEval_RestoreThread(_save);
    //end python interpreter
    Py_Finalize();
    return 0;
    }
    
    //the thread functions
    DWORD WINAPI MyThreadFunction(LPVOID lpParam)
    {
    // Non Pythonic activity
    // ...
    
    //create a new interpreter
    PyEval_AcquireLock(); // acquire lock on the GIL
    PyThreadState* pThreadState = Py_NewInterpreter();
    assert(pThreadState != NULL); // check for failure
    PyEval_ReleaseThread(pThreadState); // release the GIL
    
    
    // switch in current interpreter
    PyEval_AcquireThread(pThreadState);
    
    //execute python code
    PyRun_SimpleString("from time import time,ctime\n" "print\n"
        "print 'Today is',ctime(time())\n");
    
    // release current interpreter
    PyEval_ReleaseThread(pThreadState);
    
    //now to end the interpreter
    PyEval_AcquireThread(pThreadState); // lock the GIL
    Py_EndInterpreter(pThreadState);
    PyEval_ReleaseLock(); // release the GIL
    
    // Other non Pythonic activity
    return 0;
    }