Estoy usando el módulo ctypes
en python para cargar una biblioteca c compartida, que contiene almacenamiento local de subprocesos. Es una biblioteca c bastante grande con una larga historia, que estamos tratando de hacer seguro el hilo. La biblioteca contiene muchas variables globales y estáticas, por lo que nuestra estrategia inicial para la seguridad de subprocesos ha sido utilizar el almacenamiento local de subprocesos. Queremos que nuestra biblioteca sea independiente de la plataforma, y hemos estado compilando y probando la seguridad de subprocesos tanto en win32, win64 como en Ubuntu de 64 bits. A partir de un proceso c puro, no parece haber ningún problema.Fuga de memoria cuando se utiliza una biblioteca compartida con almacenamiento local de subprocesos a través de ctypes en un programa python
Sin embargo, en python (2.6 y 2.7) en win32 y en Ubuntu estamos viendo pérdidas de memoria. Parece que el almacenamiento local de subprocesos no se está liberando correctamente cuando finaliza un subproceso de Python. O al menos que de alguna manera el proceso de Python no es "consciente" de que la memoria se libera. El mismo problema también se ve en un programa C# en win32 en realidad, pero no está presente en nuestra máquina de prueba de servidor win64 (también se ejecuta python 2.7).
El problema se puede reproducir con un simple ejemplo de juguete como esto:
Crear una c-archivo que contiene (en linux/unix
eliminar __declspec(dllexport)
):
#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;}
Compilar ingenio MINGW
en las ventanas/gcc en linux como:
gcc -o leaky.dll
(o leaky.so
) -shared the_file.c
En Windows podríamos haber compilado con Visual Studio, reemplazando __thread
con __declspec(thread)
. Sin embargo, en win32 (hasta winXP creo), esto no funciona si la biblioteca se va a cargar en tiempo de ejecución con LoadLibrary
.
A continuación, cree un programa Python como:
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())
Eso es suficiente para reproducir el error. Importe explícitamente el recolector de basura, haciendo un collect gc.collect()
al iniciar cada nuevo subproceso no ayuda.
Por un tiempo pensé que el problema tenía que ver con los tiempos de ejecución incompatibles (python compilado con Visual Studio, mi biblioteca con MINGW
). Pero el problema también está en Ubuntu, pero no en un servidor win64, incluso cuando la biblioteca se compila de forma cruzada con MINGW
.
Espero que cualquiera pueda ayudar!
Cheers, Simon Kokkendorff, Encuesta nacional y catastro de Dinamarca.
por favor revise los errores conocidos pitón http://bugs.python.org/issue6627 http://bugs.python.org/issue3757 –
Podría liberar sus variables de fugas en hilo cerca en C? –
para arreglar esto, intente usar malloc y libre para inicializar y eliminar la matriz – pyCthon