Python PyGILState_{Ensure/Release} 在从 Python 代码返回 C++ 时导致段错误
更新好吧,看起来像在调用 PyGILState_Ensure() 之前添加 PyEval_InitThreads() 就可以了.在我急于弄清楚事情时,我错误地将我的挂起"归因于 PyEval_InitThreads().
UPDATE Well, it looks like adding PyEval_InitThreads() before the call to PyGILState_Ensure() does the trick. In my haste to figure things out I incorrectly attributed my "hanging" to PyEval_InitThreads().
但是,在阅读了一些 Python 文档后,我想知道这是否是正确的解决方案.
However, after reading some Python documentation I am wondering if this is the correct solution.
当不知道哪个线程(如果有)当前拥有全局解释器锁时调用这个函数是不安全的.
It is not safe to call this function when it is unknown which thread (if any) currently has the global interpreter lock.
<小时>
首先,我正在处理一些修改过的 GNU Radio 代码――尤其是修改过的 gr_bin_statistics_f 块.现在,有一个错误报告(尽管是旧的)几乎描述了我的确切情况.
First of all, I am working on some modified GNU Radio code - particularly a modified gr_bin_statistics_f block. Now, there is a bug report (albeit an old one) which pretty much describes my exact situation.
http://gnuradio.org/redmine/issues/show/199
现在,错误报告中提到的 usrp_spectrum_sense.py 调用 gr_bin_statistics_f (C++),然后定期回调 Python 以重新调整 USRP(无??线电).
Now, usrp_spectrum_sense.py which is mentioned in the bug report calls gr_bin_statistics_f (C++) which then calls back to Python periodically to re-tune the USRP (radio).
以下是调用 Python 代码时发生的情况:
Here is what happens when the Python code is called:
PyGILState_STATE d_gstate;
d_gstate = PyGILState_Ensure();
// call python code
PyGILState_Release(d_gstate);
因此,一旦我们从 Python 代码返回,就会在调用 PyGILState_Release(d_gstate) 时发生分段错误.虽然我的代码与原始的 gr_bin_statistics_f 之间存在差异,但似乎与此无关.
So, once we return from the Python code a segmentation fault occurs when PyGILState_Release(d_gstate) is called. While there are differences between my code and the original gr_bin_statistics_f, nothing seems to be remotely related to this.
我读到在 PyGILState_Ensure() 之前调用 PyEval_InitThreads() 已经解决了一些人的问题,但这只会导致我的程序挂起.
I read that calling PyEval_InitThreads() before PyGILState_Ensure() has solved the problem for some people, but it just causes my program to hang.
谁能帮我解释一下?或者是时候向 GNU Radio 邮件列表发送消息了?
Can anyone shed light on this for me? Or is it simply time to send a message to the GNU Radio mailing list?
在 Fedora 14 x86_64 上使用 Python2.7.
Using Python2.7 on Fedora 14 x86_64.
这是 GDB 回溯:
(gdb) c
Continuing.
[New Thread 0x7fabd3a8d700 (LWP 23969)]
[New Thread 0x7fabd328c700 (LWP 23970)]
[New Thread 0x7fabd2a8b700 (LWP 23971)]
[New Thread 0x7fabd228a700 (LWP 23972)]
[New Thread 0x7fabd1a89700 (LWP 23973)]
[New Thread 0x7fabd1288700 (LWP 23974)]
[New Thread 0x7fabd0a87700 (LWP 23975)]
[New Thread 0x7fabbbfff700 (LWP 23976)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fabbbfff700 (LWP 23976)]
0x00000036b3e0db00 in sem_post () from /lib64/libpthread.so.0
(gdb) bt
#0 0x00000036b3e0db00 in sem_post () from /lib64/libpthread.so.0
#1 0x00000036c1317679 in PyThread_release_lock () from /usr/lib64/libpython2.7.so.1.0
#2 0x00007fabd6159c1f in ~ensure_py_gil_state (this=0x2dc6fc0, x=887000000)
at gnuradio_swig_py_general.cc:5593
#3 gr_py_feval_dd::calleval (this=0x2dc6fc0, x=887000000) at gnuradio_swig_py_general.cc:5605
#4 0x00007fabd77c4b6e in gr_noise_level_f::tune_window (this=0x2db3ca0,
target_freq=) at gr_noise_level_f.cc:97
#5 0x00007fabd77c554b in gr_noise_level_f::work (this=0x2db3ca0, noutput_items=7,
input_items=, output_items=)
at gr_noise_level_f.cc:115
#6 0x00007fabd7860714 in gr_sync_block::general_work (this=0x2db3ca0,
noutput_items=, ninput_items=,
input_items=, output_items=) at gr_sync_block.cc:64
#7 0x00007fabd7846ce4 in gr_block_executor::run_one_iteration (this=0x7fabbbffed90)
at gr_block_executor.cc:299
#8 0x00007fabd7864332 in gr_tpb_thread_body::gr_tpb_thread_body (this=0x7fabbbffed90, block=...)
at gr_tpb_thread_body.cc:49
#9 0x00007fabd785cce7 in operator() (function_obj_ptr=...) at gr_scheduler_tpb.cc:42
#10 operator() (function_obj_ptr=...)
at /home/tja/Research/energy/detector/gnuradio-3.3.0/gruel/src/include/gruel/thread_body_wrapper.h:49
#11 boost::detail::function::void_function_obj_invoker0, void>::invoke (function_obj_ptr=...) at /usr/include/boost/function/function_template.hpp:153
---Type to continue, or q to quit---
#12 0x00007fabd74914ef in operator() (this=)
at /usr/include/boost/function/function_template.hpp:1013
#13 boost::detail::thread_data >::run (this=)
at /usr/include/boost/thread/detail/thread.hpp:61
#14 0x00007fabd725ca55 in thread_proxy () from /usr/lib64/libboost_thread-mt.so.1.44.0
#15 0x00000036b3e06d5b in start_thread () from /lib64/libpthread.so.0
#16 0x00000036b3ae4a7d in clone () from /lib64/libc.so.6
(gdb)
感谢收看!
推荐答案
Python 期望在任何尝试从子线程回调之前由主线程完成一定数量的初始化.
Python expects a certain amount of initialization to be done by the main thread before anything attempts to call back in from a subthread.
如果主线程是嵌入 Python 的应用程序,那么它应该在调用 Py_Initialize()
之后立即调用 PyEval_InitThreads()
.
If the main thread is an application that is embedding Python, then it should call PyEval_InitThreads()
immediately after calling Py_Initialize()
.
如果主线程是 Python 解释器本身(这里似乎就是这种情况),那么使用多线程扩展模块的模块应尽早包含导入线程"以确保 PyEval_InitThreads()
在任何子线程产生之前被正确调用.
If the main thread is instead the Python interpreter itself (as seems to be the case here), then the module using the multithreaded extension module should include an "import threading" early to ensure that PyEval_InitThreads()
is called correctly before any subthreads are spawned.
相关文章