Python C API : WindowsError after creating some number of PyObjects

标签 python python-2.7 ctypes python-c-api

我一直无法让 Python C API 给我错误。

背景: 我一直在使用 ctypes 运行 native 代码 (C++) 一段时间,但直到现在我还没有真正用 Python C 做过任何具体的事情应用程序接口(interface)。我主要只是从 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 脚本:

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 个更多 列表。

**其他细节:**

  • window 10
  • 使用 Anaconda2 32 位 Python 2.7 发行版
  • (使用该发行版中的 Python.hpython27.lib
  • python.exe --version: 2.7.13::Anaconda 自定义(32 位)
  • 使用 Visual Studio 2017 创建 DLL

只要我不从我的 C++ 代码中创建很多 PyObject,一切似乎都运行良好。我可以将 PyObject 传入和传出 Python 代码,它工作正常......直到我从我的 C++ 代码中创建了“太多”的对象。

这是怎么回事?

最佳答案

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 代码不安全。正如您所发现的,它究竟是如何失败的是不可预测的。我猜这与分配是否触发垃圾收集器运行有关,但我认为不值得花太多时间来找出确切原因。

有(至少)两种解决方案可供选择:

  1. 使用ctypes.PyDLL(文档注释类似于CDLL,只是它不释放GIL)
  2. 在您的 C++ 代码中重新获取 GIL - 一种简单的方法是:

    auto state = PyGILState_Ensure();
    // C++ code requiring GIL - probably your entire loop
    PyGILState_Release(state);
    

关于Python C API : WindowsError after creating some number of PyObjects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47310250/

相关文章:

python - 从python中的两个列表中删除公共(public)值

c - 使用 ctypes 从 Python 中的 C 函数返回的结构中访问数据

python - 优化 BeautifulSoup (Python) 代码

python - 树的数据结构

python - 使用 NumPy 生成具有变量 max/min 的随机整数序列

python - 如何使用 pytest 测试异常和错误?

python - Pygame 的 pygame.event.get() 的键值在今天是一个荒谬的数字,为什么?

python - 有什么方法可以让一个进程拥有写锁而其他进程只能并行读取?

python - 如何将字节从 ctypes 结构复制到从 create_string_buffer 创建的缓冲区

python - ctypes <类型 'exceptions.TypeError' >