2011-11-10 15 views
12

Używam moduł ctypes w Pythonie, aby załadować wspólną c-biblioteka, która zawiera pamięć lokalna wątku. Jest to dość duża biblioteka c z długą historią, którą staramy się uczynić wątkiem bezpiecznym. Biblioteka zawiera wiele zmiennych globalnych i statyki, więc naszą początkową strategią w zakresie bezpieczeństwa wątków było użycie lokalnego magazynu wątków. Chcemy, aby nasz libarary był niezależny od platformy i kompilował i testował bezpieczeństwo wątków zarówno na Win32, Win64, jak i 64-bitowym Ubuntu. Z czystego procesu c nie ma żadnych problemów.wyciek pamięci podczas korzystania z biblioteki współdzielonej pamięci lokalnej wątku poprzez ctypes w programie Pythona

Jednakże w pytona (2.6 i 2.7), na win32 i Ubuntu widzimy przecieków pamięci. Wygląda na to, że lokalny magazyn wątków nie jest poprawnie zwalniany po zakończeniu wątku python. A przynajmniej w jakiś sposób proces Pythona nie jest "świadomy" o tym, że pamięć jest wolna. Ten sam problem jest również postrzegane w C# -program na Win32 faktycznie, ale to nie jest obecny na naszym komputerze testowym serwerze Win64 (z systemem Pythona 2.7 też).

Problemem może być powielana z prostym przykładzie zabawek takich jak to:

Utwórz c-plik zawierający (na linux/unix usunąć __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;} 

Kompilacja wit MINGW na windows/gcc na linux jak:

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

Z okien mogliśmy skompilowany z Visual Studio, zastępując __thread z __declspec(thread). Jednak na win32 (do winXP wierzę), to nie działa, gdy biblioteka ma być ładowany w czasie pracy z LoadLibrary.

Teraz stworzyć program Pythona jak:

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()) 

to wystarczy do odtworzenia błędu. Jawnie zaimportuj śmieciarz, robiąc collect gc.collect() przy uruchamianiu każdego nowego wątku nie pomaga.

Przez chwilę myślałem, że problem dotyczy niekompatybilnych runtime (python skompilowany z Visual Studio, moja biblioteka z MINGW). Ale problem jest również na Ubuntu, ale nie na serwerze Win64, nawet gdy biblioteka jest cross skompilowany z MINGW.

Mam nadzieję, że każdy może pomóc!

Pozdrawiam, Simon Kokkendorff, National Survey and Cadastre of Denmark.

+0

zapoznaj znane błędy python http://bugs.python.org/issue6627 http://bugs.python.org/issue3757 –

+0

mógłbyś uwolnić nieszczelne zmiennych na zakończenie wątku w C? –

+0

to naprawić, spróbuj użyć malloc i za darmo, aby zainicjować i usunąć tablicę – pyCthon

Odpowiedz

3

Wygląda na to, że nie jest to w ogóle wina typu lub Pythona. Mogę odtworzyć ten sam wyciek, przeciekając w tym samym tempie, pisząc tylko kod C.

Co dziwne, przynajmniej na Ubuntu Linux 64, wyciek występuje, jeśli funkcja Leaker() ze zmiennymi __thread jest skompilowana jako .so i wywołana z programu z dlopen(). Nie występuje, gdy działa dokładnie ten sam kod, ale z obu części skompilowanych razem jako zwykły program C.

Podejrzewam, że usterka to jakaś interakcja między dynamicznie połączonymi bibliotekami a pamięcią lokalną wątku. Nadal wygląda raczej na zły błąd (czy to naprawdę nieudokumentowane?).

+0

Wygląda na to, że ten wątek tworzy kopię zapasową twojej teorii: http://sourceware.org/ml/libc-help/2011-04/msg00000.html –

+0

Tak - i wydaje się być zależny od systemu operacyjnego. Dostaję zachowanie na Win XP (owijając bibliotekę przez Pythona lub C#, i jak przypuszczam z C) (32-bitowe), na Ubuntu (jak sądzę, zarówno w wersji 32-bitowej, jak i 64-bitowej) - jednak na Windows Server (64-bitowy), nie widzę tego. – user1037171

1

Zgaduję, że problemem nie jest dołączanie do wątków. Ze strony człowieka dla pthread_join:

niewydolności do łączenia się z gwintem, który jest PŁYCIE (to znaczy taki, który nie jest odłączana ) wytwarza „gwint zombie”. Unikaj tego, ponieważ każdy wątek zombie zużywa pewne zasoby systemowe, a po zgromadzeniu wystarczającej liczby zombie nie będzie już można tworzyć nowych wątków (lub procesów).

Jeśli zmodyfikować pętlę zbierać przedmioty i używać .isAlive wątku() i .join() im w tym ostatnim pętli while myślę, że powinno dbać o wycieku pamięci.

+0

Dziękuję za odpowiedź. Nie wydaje mi się jednak, żeby nie dołączać do wątków. Nawet jeśli przyłączę się do każdego wątku zaraz po stworzeniu, tak że istnieje tylko jeden wątek, który działa poza wątkiem głównym, problem pozostaje. Mogę również wykryć w mojej bibliotece dll, z główną funkcją (pod oknami), że wątki odłączą się od biblioteki dll. – user1037171

+0

Alternatywnie możesz ustawić setDaemon (True) na wątku przed uruchomieniem. –