Cha*_*l72 20 python python-c-api python-c-extension python-3.x python-3.2
基本上似乎存在大量混淆/模糊性,而不PyEval_InitThreads()应该在什么时候被调用,以及需要哪些API调用.遗憾的是,官方Python文档非常模糊.关于这个主题的stackoverflow已经有很多问题了,事实上,我个人已经问了一个与这个问题几乎完全相同的问题,所以如果这个问题以复制方式结束,我不会特别感到惊讶; 但是考虑到这个问题似乎没有明确的答案.(可悲的是,我没有Guido Van Rossum的快速拨号.)
首先,让我们在这里定义问题的范围:我想做什么? 嗯...我想在C中编写一个Python扩展模块,它将:
pthread在C中使用API的Spawn工作线程好的,让我们从Python文档开始吧.在Python的3.2文档说:
void PyEval_InitThreads()
初始化并获取全局解释器锁.它应该在创建第二个线程或参与任何其他线程操作(如PyEval_ReleaseThread(tstate))之前在主线程中调用.在调用PyEval_SaveThread()或PyEval_RestoreThread()之前不需要它.
所以我的理解是:
PyEval_InitThreads()在生成任何其他线程之前从主线程调用
PyEval_InitThreads锁定GIL所以常识告诉我们,任何创建线程的C扩展模块都必须调用PyEval_InitThreads(),然后释放Global Interpreter Lock.好吧,看起来很简单.所以初步,所有的需要将以下代码:
PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */
Run Code Online (Sandbox Code Playgroud)
看起来很简单......但不幸的是,Python 3.2文档也说PyEval_ReleaseLock已经弃用了.相反,我们应该使用PyEval_SaveThread以释放GIL:
PyThreadState*PyEval_SaveThread()
释放全局解释器锁(如果已创建并启用了线程支持)并将线程状态重置为NULL,则返回先前的线程状态(不是NULL).如果已创建锁,则当前线程必须已获取它.
呃......好吧,我想C扩展模块需要说:
PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();
Run Code Online (Sandbox Code Playgroud)
实际上,这正是这个stackoverflow回答所说的.除非我在实践中尝试这样做,否则当我导入扩展模块时,Python解释器会立即出现错误.尼斯.
好的,现在我放弃官方的Python文档并转向谷歌.所以,这个随机的博客声称你需要做的就是扩展模块PyEval_InitThreads().当然,文档声称PyEval_InitThreads()取得GIL,而事实上,一对源代码的快速检查PyEval_InitThreads()中ceval.c发现,它确实调用内部函数take_gil(PyThreadState_GET());
所以PyEval_InitThreads() 一定要收购GIL.我会认为你在调用之后绝对需要以某种方式释放GIL PyEval_InitThreads(). 但是怎么样? PyEval_ReleaseLock()不推荐使用,PyEval_SaveThread()只是莫名其妙的段错误.
好的...所以也许由于某些原因,目前我的理解不多,C扩展模块不需要发布GIL.我尝试了......并且,正如预期的那样,一旦另一个线程尝试获取GIL(使用PyGILState_Ensure),程序就会从死锁中挂起.所以是的...你真的需要在打电话后释放GIL PyEval_InitThreads().
所以再一次,问题是:你怎么在打电话后释放GIL PyEval_InitThreads()?
更一般地说:C-extension模块究竟需要做什么才能从工作者C线程安全地调用Python代码?
use*_*342 12
你的理解是正确的: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();
Run Code Online (Sandbox Code Playgroud)
如果您的代码手动创建线程,他们需要在执行任何与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);
Run Code Online (Sandbox Code Playgroud)
执行 C/Python API 时有两种多线程方法。
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;
}
Run Code Online (Sandbox Code Playgroud)
代码如下
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;
}
Run Code Online (Sandbox Code Playgroud)
需要注意的是,全局解释器锁仍然存在,尽管为每个线程提供了单独的解释器,但在执行python时,我们仍然一次只能执行一个线程。GIL对PROCESS是唯一的,所以尽管为每个线程提供了唯一的子解释器,但我们不能同时执行线程
资料来源:在主线程中执行一个 Python 解释器,对于每个线程,我们可以给它自己的子解释器
小智 5
我见过类似的症状:如果仅调用PyEval_InitThreads(),则会死锁,因为我的主线程再也不会从Python调用任何东西;如果我无条件地调用类似PyEval_SaveThread()的事件,则会出现segfaults。症状取决于Python的版本和情况:我正在开发一个插件,该插件将Python嵌入到可作为Python扩展的一部分加载的库中。因此,代码需要独立于Python是否作为主要代码来运行。
以下代码适用于python2.7和python3.4,以及我的库在Python内和Python外运行。在主线程中执行的插件初始化例程中,运行:
Py_InitializeEx(0);
if (!PyEval_ThreadsInitialized()) {
PyEval_InitThreads();
PyThreadState* mainPyThread = PyEval_SaveThread();
}
Run Code Online (Sandbox Code Playgroud)
(mainPyThread实际上是一些静态变量,但我认为这并不重要,因为我不再需要再次使用它)。
然后,我使用pthreads创建线程,并在需要访问Python API的每个函数中使用:
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
// Python C API calls
PyGILState_Release(gstate);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6203 次 |
| 最近记录: |