2011-11-10 30 views
12

Tôi đang sử dụng mô-đun ctypes trong python để tải một thư viện c được chia sẻ, chứa lưu trữ cục bộ luồng. Nó là một thư viện c khá lớn với một lịch sử lâu dài, rằng chúng tôi đang cố gắng để làm cho thread an toàn. Thư viện chứa rất nhiều biến và thống kê toàn cầu, vì vậy chiến lược ban đầu của chúng tôi hướng tới an toàn luồng đã được sử dụng lưu trữ cục bộ luồng. Chúng tôi muốn libarary của chúng tôi được nền tảng độc lập, và đã được biên dịch và thử nghiệm an toàn thread trên cả hai win32, win64 và 64-bit Ubuntu. Từ một quy trình thuần túy, dường như không có vấn đề gì.Rò rỉ bộ nhớ khi sử dụng thư viện được chia sẻ với bộ nhớ cục bộ qua ctypes trong chương trình python

Tuy nhiên trong python (2.6 và 2.7) trên win32 và trên Ubuntu, chúng tôi thấy rò rỉ bộ nhớ. Dường như lưu trữ cục bộ luồng không được phát hành đúng cách khi một chuỗi python chấm dứt. Hoặc ít nhất bằng cách nào đó quá trình python không "nhận biết" về việc bộ nhớ được giải phóng. Cùng một vấn đề cũng được nhìn thấy trong một chương trình C# trên win32 thực sự, nhưng nó không phải là hiện tại trên máy chủ thử nghiệm win64 của chúng tôi (chạy python 2,7 cũng).

vấn đề này có thể được sao chép với một ví dụ đồ chơi đơn giản như thế này:

Tạo một c-file chứa (trên linux/unix loại bỏ __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;} 

Compile wit MINGW trên cửa sổ/gcc trên linux như:

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

Trên cửa sổ chúng tôi có thể đã biên soạn với Visual Studio, thay thế __thread bằng __declspec(thread). Tuy nhiên trên win32 (lên đến winXP tôi tin), điều này không hoạt động nếu thư viện được nạp trong thời gian chạy với LoadLibrary.

Bây giờ tạo ra một chương trình python như:

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

Đó là đủ để tạo lại lỗi. Nhập khẩu rõ ràng bộ thu gom rác, thực hiện collect gc.collect() khi bắt đầu mỗi chuỗi mới không có tác dụng.

Trong một thời gian, tôi nghĩ rằng vấn đề phải làm với thời gian chạy không tương thích (python được biên dịch bằng Visual Studio, thư viện của tôi với MINGW). Nhưng vấn đề cũng là trên Ubuntu, nhưng không phải trên một máy chủ win64, ngay cả khi thư viện được biên dịch chéo với MINGW.

Hy vọng rằng mọi người đều có thể trợ giúp!

Chúc mừng, Simon Kokkendorff, Khảo sát quốc gia và địa chính của Đan Mạch.

+0

xin vui lòng xem xét biết lỗi python http://bugs.python.org/issue6627 http://bugs.python.org/issue3757 –

+0

Ông có thể giải phóng các biến bị rò rỉ của bạn về chủ đề gần gũi trong C? –

+0

để khắc phục điều này, hãy thử sử dụng malloc và miễn phí để khởi tạo và loại bỏ mảng – pyCthon

Trả lời

3

Điều này có vẻ không phải là lỗi của ctypes 'hoặc Python. Tôi có thể tái tạo cùng một rò rỉ, rò rỉ ở mức tương tự, bằng cách chỉ viết mã C.

Kỳ lạ, ít nhất là trên Ubuntu Linux 64, rò rỉ xảy ra nếu hàm Leaker() với biến __thread được biên dịch dưới dạng .so và được gọi từ chương trình có dlopen(). Nó không xảy ra khi chạy chính xác cùng một mã nhưng với cả hai phần được biên dịch với nhau như một chương trình C thông thường.

Tôi nghi ngờ rằng lỗi là một số tương tác giữa các thư viện được liên kết động và bộ nhớ cục bộ. Tuy nhiên, nó trông giống như một lỗi khá xấu (nó thực sự không có giấy tờ?).

+0

Dường như chuỗi này sao lưu lý thuyết của bạn: http://sourceware.org/ml/libc-help/2011-04/msg00000.html –

+0

Yep - và nó có vẻ phụ thuộc vào hệ điều hành. Tôi nhận được hành vi trên Win XP (gói thư viện bởi python hoặc C#, và tôi đoán như bạn lưu ý từ C) (32 bit), trên Ubuntu (cả 32 bit và 64-bit, tôi tin) - tuy nhiên trên Windows Server (64 bit), tôi không thấy. – user1037171

1

Đoán của tôi là không tham gia với chủ đề là vấn đề. Từ trang người đàn ông cho pthread_join:

Không tham gia với một chuỗi có thể tham gia (tức là không được nối với nhau) (tạo ra một chuỗi zombie). Tránh làm điều này, vì mỗi chủ đề zombie tiêu thụ một số tài nguyên hệ thống và khi đủ số chuỗi tích lũy, bạn sẽ không còn có thể tạo chủ đề mới (hoặc quy trình) nữa.

Nếu bạn sửa đổi vòng lặp của bạn để thu thập các đối tượng chuỗi và sử dụng .isAlive() và .join() trên chúng trong vòng lặp cuối cùng đó, tôi nghĩ rằng nó sẽ chăm sóc rò rỉ bộ nhớ của bạn.

+0

Cảm ơn câu trả lời của bạn. Tôi không có vẻ như không tham gia các chủ đề là vấn đề mặc dù. Ngay cả khi tôi tham gia tất cả các chuỗi ngay sau khi tạo, để chỉ có một luồng chạy ngoài chủ đề chính, vấn đề vẫn còn. Tôi cũng có thể phát hiện trong dll của tôi, với một chức năng chính (dưới cửa sổ) mà các chủ đề tách ra từ dll. – user1037171

+0

cách khác bạn có thể đặt setDaemon (True) trên một chuỗi trước khi bạn bắt đầu –

Các vấn đề liên quan