python3中的PyEval_InitThreads:如何/何时调用它?(故事继续,令人恶心)
基本上,对于什么时候应该调用以及需要什么样的API调用,似乎存在着大量的混淆/歧义。不幸的是,这个问题非常模糊。关于这个话题已经有很多,事实上,我个人已经讨论过这个话题,所以如果这是一个重复的话题,我不会特别惊讶;但是考虑到这个问题似乎没有明确的答案。(遗憾的是,我没有Guido Van Rossum的快速拨号。) 首先,让我们在这里定义问题的范围:我想做什么?好吧。。。我想用C编写一个Python扩展模块,它将: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回调
pthread
API生成工作线程PyEval_InitThreads()
在任何其他线程之前从主线程开始
产卵PyEval\u InitThreads
锁定GILPyEval_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;
}