You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用ctypes调用C DLL遇栈溢出,能否像C#一样分配线程栈内存?

解决ctypes调用C DLL时的栈溢出问题

首先得明确问题根源:你的C函数里在栈上声明了c_out inner_out,这个结构体的实际大小算下来接近4MB(50个数组×每个数组1000个元素×每个元素10个double),而Windows默认线程栈大小仅1MB左右——Python主线程用的就是这个默认栈,直接调用自然触发栈溢出。C#里你能解决是因为创建了自定义栈大小的新线程,Python里也有两种可行方案:

方案一:修改C代码(推荐)

最稳妥的方式是把栈上的大结构体改成堆分配,彻底摆脱线程栈大小的限制,跨平台兼容性也更好。修改你的C实现代码如下:

#include "ctypes_testT.h"
#include <stdlib.h>  // 引入malloc/free

__declspec(dllexport) void _stdcall dll_ctypes_test(double in, c_out *out) {
    // 在堆上分配内存,避免占用线程栈
    c_out *inner_out = (c_out*)malloc(sizeof(c_out));
    if (inner_out == NULL) {
        // 处理内存分配失败的异常情况
        return;
    }

    // 执行你的计算逻辑,操作inner_out->xxx
    inner_out->n_arrays = N_ARRAYS;
    for (int i = 0; i < N_ARRAYS; i++) {
        inner_out->arrays[i].n_elements = N_ELEMENTS;
        // 填充元素数据...
    }

    // 将计算结果复制到输出参数
    *out = *inner_out;

    // 释放堆内存,避免泄漏
    free(inner_out);
}

方案二:在Python中创建大栈线程调用

如果无法修改C代码,可以在Python里创建一个带自定义栈大小的线程来执行DLL调用。Python的threading.Thread没有直接设置栈大小的参数,但底层的_thread模块的start_new_thread函数支持指定stacksize参数,示例代码如下:

import ctypes
import _thread
from threading import Event

N_ELEMENTS = 1000
N_ARRAYS = 50

class element(ctypes.Structure):
    _fields_ = [('var_0', ctypes.c_double), ('var_1', ctypes.c_double),
                ('var_2', ctypes.c_double), ('var_3', ctypes.c_double),
                ('var_4', ctypes.c_double), ('var_5', ctypes.c_double),
                ('var_6', ctypes.c_double), ('var_7', ctypes.c_double),
                ('var_8', ctypes.c_double), ('var_9', ctypes.c_double)]

class arr(ctypes.Structure):
    _fields_ = [('n_elements', ctypes.c_int), ('elements', element * N_ELEMENTS)]

class c_out(ctypes.Structure):
    _fields_ = [('n_arrays', ctypes.c_int), ('arrays', arr * N_ARRAYS)]

dll = ctypes.WinDLL(r'C:\repos\ctypes_test\x64\Debug\ctypes_test.dll')
dll.dll_ctypes_test.argtypes = [ctypes.c_double, ctypes.POINTER(c_out)]
dll.dll_ctypes_test.restype = None

# 准备输出数据结构
output = c_out()
# 创建事件用于线程同步
done_event = Event()

def call_dll(in_val, out_ptr, event):
    try:
        dll.dll_ctypes_test(in_val, out_ptr)
    finally:
        event.set()

# 创建新线程,指定栈大小为20MB(和你C#里的设置一致)
_thread.start_new_thread(call_dll, (5, ctypes.byref(output), done_event), stacksize=20*1024*1024)

# 等待线程执行完成
done_event.wait()

# 现在可以正常使用output中的数据
print(f"处理完成,共收到 {output.n_arrays} 个数组")

注意:stacksize参数单位是字节,该设置仅在Windows平台有效,Linux等其他平台可能受系统配置限制;另外_thread属于底层模块,使用时需注意线程安全问题。

总结来说,方案一从根源上解决了栈依赖问题,是更优选择;方案二适合无法修改C代码的临时场景。

内容的提问来源于stack exchange,提问作者mortysporty

火山引擎 最新活动