12

Python でモジュールを使用して、ctypesスレッド ローカル ストレージを含む共有 c-library をロードしています。これは長い歴史を持つ非常に大きな c-library であり、スレッドセーフにしようとしています。ライブラリには多くのグローバル変数と静的変数が含まれているため、スレッド セーフに対する最初の戦略は、スレッド ローカル ストレージを使用することでした。ライブラリをプラットフォームに依存しないようにしたいと考えており、win32、win64、および 64 ビット Ubuntu の両方でスレッド セーフをコンパイルおよびテストしています。純粋な c プロセスからは、問題はないようです。

ただし、win32 および Ubuntu の Python (2.6 および 2.7) では、メモリ リークが発生しています。Python スレッドが終了したときに、スレッド ローカル ストレージが適切に解放されていないようです。または、少なくとも、メモリが解放されたことをpythonプロセスが「認識」していないということです。同じ問題は、実際には win32 の ac#-program でも見られますが、win64 サーバー テスト マシン (python 2.7 も実行) には存在しません。

この問題は、次のような単純なおもちゃの例で再現できます。

以下を含む c ファイルを作成します (linux/unix削除__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;}

MINGW次のように、Linux の Windows/gcc でウィットをコンパイルします。

gcc -o leaky.dll(またはleaky.so)-shared the_file.c

Windows では、Visual Studio でコンパイルして__thread__declspec(thread). ただし、win32 (winXP までだと思います) では、ライブラリを実行時に .xml でロードする場合、これは機能しません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()ます。新しいスレッドを開始するたびに a を実行しても効果はありません。

しばらくの間、この問題は互換性のないランタイム (Visual Studio でコンパイルされた Python、私のライブラリではMINGW) に関係していると考えていました。しかし、問題はUbuntuにもありますが、ライブラリがMINGW.

誰でも助けてくれることを願っています!

乾杯、Simon Kokkendorff、National Survey and Cadastre of Denmark。

4

2 に答える 2

3

これは、ctypes や Python のせいではないようです。Cコードを書くだけで、同じリーク、同じレートでのリークを再現できます。

奇妙なことに、少なくとも Ubuntu Linux 64 では、__thread 変数を持つ Leaker() 関数が .so としてコンパイルされ、dlopen() を使用してプログラムから呼び出されると、リークが発生します。まったく同じコードを実行しているが、両方の部分が通常の C プログラムとして一緒にコンパイルされている場合は発生しません。

障害は、動的にリンクされたライブラリとスレッド ローカル ストレージの間の相互作用にあると思われます。それでも、かなり悪いバグのように見えます (本当に文書化されていないのでしょうか?)。

于 2012-07-26T23:47:33.387 に答える
1

私の推測では、スレッドに参加していないことが問題です。pthread_join の man ページから:

結合可能なスレッド (つまり、切り離されていないスレッド) との結合に失敗すると、「ゾンビ スレッド」が生成されます。各ゾンビ スレッドはシステム リソースを消費し、ゾンビ スレッドが十分に蓄積されると、新しいスレッド (またはプロセス) を作成できなくなるため、これは避けてください。

ループを変更してスレッド オブジェクトを収集し、最後の while ループで .isAlive() と .join() を使用すると、メモリ リークを処理する必要があると思います。

于 2011-11-29T05:01:27.433 に答える