Python C API: 创建一定数量的 PyObject 后出现 WindowsError
Python C API: WindowsError after creating some number of PyObjects
我一直在让 Python C API 不给我错误。
背景: 我一直在使用 ctypes
到 运行 本机代码 (C++) 一段时间,但直到现在我还没有真正做过Python C API 的任何特定内容。我主要只是从 Python 传递结构并从 C++ 填充它们。我使用结构的方式变得很麻烦,所以我决定尝试直接在 C++ 中创建 Python 对象,然后将它们传回我的 Python 脚本。
代码:
我有一个只有一个功能的 DLL (Foo.dll
):
#define N 223
__declspec(dllexport) void Bar(void)
{
std::cout << "Bar" << std::endl;
for (int i = 0; i < N; ++i)
{
auto list = PyList_New(0);
std::cout << "Created: " << i << std::endl;
//Py_DECREF(list);
}
}
然后我有 Python 脚本我 运行ning:
import ctypes as C
dll = r"C:\path\to\dll\Foo.dll"
Foo = C.CDLL(dll)
# lists = [[] for _ in range(0)]
Foo.Bar()
print "Done."
会发生什么: 如果我在上面的 DLL 中将 N
定义为 222
或以下,代码工作正常(除了内存泄漏,但这不是问题)。
如果我取消注释 //Py_DECREF(list)
行,代码就可以正常工作。
但是,通过上面的代码,我得到了这个:
Bar
Created: 0
Created: 1
Created: 2
...(omitted for your sake)
Created: 219
Created: 220
Created: 221
Traceback (most recent call last):
File "C:\path_to_script\script.py", line 9, in <module>
Foo.Bar()
WindowsError: exception: access violation reading 0x00000028
事实上,我用字典、列表、元组等得到了同样的结果。如果我创建一个列表然后将空子列表附加到该列表,我会得到相同的结果。
更奇怪的是,我在实际 Python 脚本中制作的每个列表都会减少 DLL 在出现此 windows 错误之前可以制作的列表数量。
更奇怪的是,如果我在我的 python 脚本中创建超过 222 个列表,那么 DLL 在创建类似 720 more 之前不会遇到此错误列出。
**其他细节:**
- Windows 10
- 使用 Anaconda2 32 位 Python 2.7 发行版
- (使用来自该分布的
Python.h
和 python27.lib
- python.exe --版本:
2.7.13 :: Anaconda custom (32-bit)
- 使用 Visual Studio 2017
创建 DLL
只要我不从我的 C++ 代码中创建很多 PyObject
,一切似乎都运行良好。我可以在 Python 代码之间传递 PyObject
s 并且它工作正常..直到我从我的 C++ 代码中创建了 "too many" 个对象。
这是怎么回事?
From the documentation for CDLL:
The Python global interpreter lock is released before calling any function exported by these libraries, and reacquired afterwards.
这使得 不安全 使用 Python C API 代码。正如您所发现的,它究竟是如何失败的是不可预测的。我猜这与分配是否触发垃圾收集器的 运行 有关,但我认为不值得花太多时间来找出确切原因。
有(至少)两种解决方案可供选择:
- 使用
ctypes.PyDLL
(文档注释类似于CDLL
,只是它不释放GIL)
在您的 C++ 代码中重新获取 GIL - 一个简单的方法是:
auto state = PyGILState_Ensure();
// C++ code requiring GIL - probably your entire loop
PyGILState_Release(state);
我一直在让 Python C API 不给我错误。
背景: 我一直在使用 ctypes
到 运行 本机代码 (C++) 一段时间,但直到现在我还没有真正做过Python C API 的任何特定内容。我主要只是从 Python 传递结构并从 C++ 填充它们。我使用结构的方式变得很麻烦,所以我决定尝试直接在 C++ 中创建 Python 对象,然后将它们传回我的 Python 脚本。
代码:
我有一个只有一个功能的 DLL (Foo.dll
):
#define N 223
__declspec(dllexport) void Bar(void)
{
std::cout << "Bar" << std::endl;
for (int i = 0; i < N; ++i)
{
auto list = PyList_New(0);
std::cout << "Created: " << i << std::endl;
//Py_DECREF(list);
}
}
然后我有 Python 脚本我 运行ning:
import ctypes as C
dll = r"C:\path\to\dll\Foo.dll"
Foo = C.CDLL(dll)
# lists = [[] for _ in range(0)]
Foo.Bar()
print "Done."
会发生什么: 如果我在上面的 DLL 中将 N
定义为 222
或以下,代码工作正常(除了内存泄漏,但这不是问题)。
如果我取消注释 //Py_DECREF(list)
行,代码就可以正常工作。
但是,通过上面的代码,我得到了这个:
Bar
Created: 0
Created: 1
Created: 2
...(omitted for your sake)
Created: 219
Created: 220
Created: 221
Traceback (most recent call last):
File "C:\path_to_script\script.py", line 9, in <module>
Foo.Bar()
WindowsError: exception: access violation reading 0x00000028
事实上,我用字典、列表、元组等得到了同样的结果。如果我创建一个列表然后将空子列表附加到该列表,我会得到相同的结果。
更奇怪的是,我在实际 Python 脚本中制作的每个列表都会减少 DLL 在出现此 windows 错误之前可以制作的列表数量。
更奇怪的是,如果我在我的 python 脚本中创建超过 222 个列表,那么 DLL 在创建类似 720 more 之前不会遇到此错误列出。
**其他细节:**
- Windows 10
- 使用 Anaconda2 32 位 Python 2.7 发行版
- (使用来自该分布的
Python.h
和python27.lib
- python.exe --版本:
2.7.13 :: Anaconda custom (32-bit)
- 使用 Visual Studio 2017 创建 DLL
只要我不从我的 C++ 代码中创建很多 PyObject
,一切似乎都运行良好。我可以在 Python 代码之间传递 PyObject
s 并且它工作正常..直到我从我的 C++ 代码中创建了 "too many" 个对象。
这是怎么回事?
From the documentation for CDLL:
The Python global interpreter lock is released before calling any function exported by these libraries, and reacquired afterwards.
这使得 不安全 使用 Python C API 代码。正如您所发现的,它究竟是如何失败的是不可预测的。我猜这与分配是否触发垃圾收集器的 运行 有关,但我认为不值得花太多时间来找出确切原因。
有(至少)两种解决方案可供选择:
- 使用
ctypes.PyDLL
(文档注释类似于CDLL
,只是它不释放GIL) 在您的 C++ 代码中重新获取 GIL - 一个简单的方法是:
auto state = PyGILState_Ensure(); // C++ code requiring GIL - probably your entire loop PyGILState_Release(state);