gpt4 book ai didi

python - 在 python 程序中通过 ctypes 使用具有线程本地存储的共享库时发生内存泄漏

转载 作者:太空狗 更新时间:2023-10-29 17:50:40 25 4
gpt4 key购买 nike

我在 python 中使用 ctypes 模块来加载共享的 c-library ,其中包含线程本地存储。它是一个相当大的 C 库,历史悠久,我们正在努力使线程安全。该库包含大量全局变量和静态变量,因此我们最初的线程安全策略是使用线程本地存储。我们希望我们的库独立于平台,并且一直在 win32、win64 和 64 位 Ubuntu 上编译和测试线程安全。从纯 c 进程来看,似乎没有任何问题。

但是在 win32 和 Ubuntu 上的 python(2.6 和 2.7)中,我们看到内存泄漏。当 python 线程终止时,线程本地存储似乎没有被正确释放。或者至少 python 进程不知何故“不知道”内存已被释放。实际上,在 win32 上的 c# 程序中也出现了同样的问题,但在我们的 win64 服务器测试机(也运行 python 2.7)上没有出现。

问题可以用像这样的简单玩具示例重现:

创建一个包含(在 linux/unix 上删除 __declspec(dllexport))的 c 文件:

#include <stdio.h>
#include <stdlib.h>
void __declspec(dllexport) Leaker(int tid){
static __thread double leaky[1024];
static __thread int init=0;
if (!init){
printf("Thread %d initializing.", tid);
int i;
for (i=0;i<1024;i++) leaky[i]=i;
init=1;}
else
printf("This is thread: %d\n",tid);
return;}

在 Windows 上使用 MINGW 进行编译/在 Linux 上使用 gcc 编译,例如:

gcc -o leaky.dll(或leaky.so)-shared the_file.c

在 Windows 上,我们可以使用 Visual Studio 进行编译,将 __thread 替换为 __declspec(thread)。但是在 win32 上(我相信直到 winXP),如果要在运行时使用 LoadLibrary 加载库,这将不起作用。

现在创建一个 python 程序,如下所示:

import threading, ctypes, sys, time
NRUNS=1000
KEEP_ALIVE=5
REPEAT=2
lib=ctypes.cdll.LoadLibrary("leaky.dll")
lib.Leaker.argtypes=[ctypes.c_int]
lib.Leaker.restype=None
def UseLibrary(tid,repetitions):
for i in range(repetitions):
lib.Leaker(tid)
time.sleep(0.5)
def main():
finished_threads=0
while finished_threads<NRUNS:
if threading.activeCount()<KEEP_ALIVE:
finished_threads+=1
thread=threading.Thread(target=UseLibrary,args=(finished_threads,REPEAT))
thread.start()
while threading.activeCount()>1:
print("Active threads: %i" %threading.activeCount())
time.sleep(2)
return
if __name__=="__main__":
sys.exit(main())

这足以重现错误。显式导入垃圾收集器,在启动每个新线程时执行 collect gc.collect() 没有帮助。

有一段时间我认为问题与不兼容的运行时有关(python 使用 Visual Studio 编译,我的库使用 MINGW)。但问题也出现在 Ubuntu 上,但不是在 win64 服务器上,即使库是使用 MINGW 交叉编译的。

希望大家帮帮忙!

干杯,Simon Kokkendorff,丹麦国家调查和地籍局。

最佳答案

这似乎根本不是 ctypes 或 Python 的错。我可以通过仅编写 C 代码来重现相同的泄漏,以相同的速率泄漏。

奇怪的是,至少在 Ubuntu Linux 64 上,如果带有 __thread 变量的 Leaker() 函数被编译为 .so 并从带有 dlopen() 的程序中调用,就会发生泄漏。当运行完全相同的代码但将两部分一起编译为常规 C 程序时,不会发生这种情况。

我怀疑错误是动态链接库和线程本地存储之间的某种交互。不过,它看起来像是一个相当严重的错误(它真的没有记录吗?)。

关于python - 在 python 程序中通过 ctypes 使用具有线程本地存储的共享库时发生内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8076752/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com