基本上,对于究竟什么时候应该调用,以及需要什么伴随的API调用,似乎存在着大量的混淆/模糊。不幸的是,正式Python文档非常模糊。关于这个话题,已经有关于堆栈溢出的几个问题了,事实上,我个人已经对这个主题进行了问了一个几乎相同的问题,所以如果这是一个重复,我不会感到特别惊讶;但是考虑到这个问题似乎没有明确的答案。(遗憾的是,我没有快速拨号的吉多·范罗苏姆( Guido Van Rossum )。)
首先,让我们在这里定义问题的范围:我想做什么?好……我想用C编写一个Python扩展模块,它将:
pthread API生成工作线程好的,让我们从Python文档本身开始。Python3.2文档说:
void PyEval_InitThreads() 初始化并获取全局解释器锁。在创建第二个线程或执行任何其他线程操作(如PyEval_ReleaseThread(tstate) )之前,应该在主线程中调用它。在调用PyEval_SaveThread()或PyEval_RestoreThread()之前不需要它。
所以我在这里的理解是:
PyEval_InitThreads()。PyEval_InitThreads锁定GIL所以常识告诉我们,任何创建线程的C扩展模块都必须调用PyEval_InitThreads(),然后释放全局解释器锁。好吧,看起来很简单。从表面上看,所需的只是以下代码:
PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */似乎很容易..。但是不幸的是,Python3.2docs也说已弃用。相反,我们应该使用PyEval_SaveThread来发布GIL:
PyThreadState* PyEval_SaveThread() 释放全局解释器锁(如果已经创建并且启用了线程支持),并将线程状态重置为NULL,返回以前的线程状态(非空)。如果已创建锁,则当前线程必须已获得锁。
呃..。好吧,我想C扩展模块需要说:
PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();事实上,这正是这个堆叠溢出的答案所说的。除非我在实践中实际尝试了这一点,否则Python解释器在导入扩展模块时会立即出错。好的。
好了,现在我放弃了Python的官方文档,转而使用Google。因此,这个随机博客声称您需要从扩展模块中调用PyEval_InitThreads()。当然,文档声称PyEval_InitThreads()获得了GIL,实际上,揭示了它确实调用了内部函数take_gil(PyThreadState_GET());。
所以PyEval_InitThreads()肯定买下了GIL。那么,我认为您绝对需要在调用PyEval_InitThreads()之后以某种方式释放GIL。但是怎么做呢? PyEval_ReleaseLock()被否决了,而PyEval_SaveThread()只是莫名其妙地错了.
好吧..。因此,也许出于目前无法理解的原因,C扩展模块不需要发布GIL。我试过了..。而且,正如预期的那样,一旦另一个线程试图获得GIL (使用PyGILState),程序就会挂起死锁。所以是的..。您确实需要在调用PyEval_InitThreads()之后释放GIL。
因此,问题是:在调用http://docs.python.org/3.2/c-api/init.html#PyEval_InitThreads之后,如何释放GIL?
更广泛地说:C扩展模块需要做什么才能安全地从工作C线程调用Python代码?
发布于 2013-03-18 07:16:42
您的理解是正确的:调用PyEval_InitThreads可以获得GIL。在正确编写的Python/C应用程序中,这不是问题,因为GIL将被及时解锁,无论是自动的还是手动的。
如果主线程继续运行Python代码,则没有什么特别的事情要做,因为Python解释器将在执行了许多指令后自动放弃GIL (允许另一个线程获取它,这将再次放弃它,等等)。此外,每当Python准备调用阻塞系统调用(例如从网络读取或写入文件)时,它都会在调用周围释放GIL。
这个答案的原始版本在这里结束了。但是还有一件事需要考虑:嵌入场景。
当嵌入Python时,主线程通常初始化Python并继续执行其他与Python无关的任务。在这个场景中,没有任何东西会自动释放GIL,所以这必须由线程本身来完成。这并不是特定于调用PyEval_InitThreads的调用,而是期望使用GIL获得的调用来调用所有Python/C代码。
例如,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_INCREF一样简单。要做到这一点,请使用以下是
// Acquire the GIL
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
... call Python code here ...
// Release the GIL. No Python API allowed beyond this point.
PyGILState_Release(gstate);发布于 2017-03-08 09:36:06
在执行C/Python时有两种多线程方法。
1.使用相同解释器执行不同线程--我们可以执行Python解释器,并在不同线程上共享相同的解释器。
编码如下。
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;
}需要注意的是,Global解释器锁仍然存在,尽管为每个线程提供了单独的解释器,但是当涉及到python执行时,我们仍然只能一次执行一个线程。GIL是 unique to PROCESS,因此尽管为每个线程提供了唯一的子解释器,但我们不能同时执行线程。
发布于 2015-06-17 10:20:50
我看到了类似于您的症状:如果我只调用PyEval_InitThreads(),就会出现死锁,因为我的主线程再也不会从Python中调用任何东西,如果我无条件地调用类似于PyEval_SaveThread()的东西,则会出现分段错误。症状取决于Python的版本和情况:我正在开发一个插件,将Python嵌入到一个库中,该库可以作为Python扩展的一部分加载。因此,代码需要独立运行,而不依赖于Python是否将其作为main加载。
下面的代码用于python2.7和python3.4,我的库运行在Python内部和Python外部。在我在主线程中执行的插件init例程中,我运行:
Py_InitializeEx(0);
if (!PyEval_ThreadsInitialized()) {
PyEval_InitThreads();
PyThreadState* mainPyThread = PyEval_SaveThread();
}(mainPyThread实际上是一些静态变量,但我认为这并不重要,因为我再也不需要使用它)。
然后,我使用p线程创建线程,在每个需要访问Python的函数中,我使用:
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
// Python C API calls
PyGILState_Release(gstate);https://stackoverflow.com/questions/15470367
复制相似问题