2013-03-04 53 views
6

私はC言語でpthreadsを使い始めています。私は可能な限り、自分のコードを "バグフリー"として書きます。c pthreads + valgrind =メモリリーク:なぜですか?

  1. 私は私が完了した時点で参加合流可能スレッドを作成(コードスニペット1)
  2. は私が合流可能な作成:余分な私はメモリリークしていますのでご注意ください、valgrindのは私に言っているにかかわらず、天候になろうとしているにもかかわらず

    私が作成した後、切り離しスレッド(コードスニペット2)

  3. 私がデタッチスレッドを作成(コードスニペット3)私は、これはすでに議論されている知っている

(を参照してください、thisともthis)が、私はするとして、まだ好奇心:

私はエラーなしで終わる特定の実行に
  1. なぜ?
  2. デタッチされたスレッドを扱うとき、なぜmallocs()全体の乱数があるようですか? < <アンサーで提供されているコードスニペットmain()の遅延が追加されたコードスニペット
  3. 切り離されたスレッドを扱うときに「メモリリーク」が発生するのはなぜですか?私は以前の回答やvalgrindのトレースから理解では、のpthread_create()は、このように、いくつかの欠落を、必要に応じてと時間でそれを再利用するスレッドが使用するスタックを拡張し、根本的な原因である2

と同じ< <解放する。しかし、あまり明確でないのは、実行の実行に依存する理由と、分離されたスレッドを作成するときに発生する理由です。特定の解答、コメント、そしてその人から見たように、分離されたスレッドのリソースはスレッドの完了時に解放されます。私はこれを回避するためにさまざまな調整を試みました(各スレッドの終わりの前に、メインスレッドの終わりの前にスリープ時間を追加し、スタックサイズを増やし、 "作業"を追加しました...)最終結果は多く。また、分離されたスレッドを処理する際に、なぜ全体的な "mallocs()"の乱数があるのですか?valgrindは分離されたスレッドの一部を失いますか?これはスタックサイズに依存しないようにも見えます。

提供されているコードは、スレッド管理へのjoinable/join()アプローチがより適切なimhoのように見えるマネージャ/ワーカーモデルのモックの例です。

啓発のおかげで、提供できる可能性があります。私はまた、これらの(コメント以上の)コードスニペットが、pthreadsを使い始めることを望む人に役立つことを願っています。

- swappy

PSのSys情報:Debianの64ビットのアーチ上のGCC

コード(合流可能スレッドが参加)1をスニペット:

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 4 extra malloc vs free (most frequently) 
    The number of mallocs() is more conservative and depends on the number of threads. 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
    int status; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* Properly exit with status code tid */ 
    pthread_exit((void *)(&container->status)); 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    void *return_status;      /* Will hold return status */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread joinable attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     args[tid].status = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Properly join() all workers before completion */ 
    for(tid = 0; tid < MAX_THREADS; tid++) 
    { 
     return_code = pthread_join(workers[tid], &return_status); 
     if (return_code != 0) 
     { 
      printf("[ERROR] Return code from pthread_join() is %d\n", return_code); 
      return EXIT_FAILURE; 
     } 
     printf("Thread %d joined with return status %d\n", tid, *(int *)return_status); 
    } 

    return EXIT_SUCCESS; 
} 

コードは2(一戸建てスレッドスニペット作成後):

コードスニペット1(接合スレッド& MEM-リーク)

==27802== 
==27802== HEAP SUMMARY: 
==27802==  in use at exit: 1,558 bytes in 4 blocks 
==27802== total heap usage: 105 allocs, 101 frees, 28,814 bytes allocated 
==27802== 
==27802== Searching for pointers to 4 not-freed blocks 
==27802== Checked 104,360 bytes 
==27802== 
==27802== 36 bytes in 1 blocks are still reachable in loss record 1 of 4 
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400894D: _dl_map_object (dl-load.c:162) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130) 
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265) 
==27802== 
==27802== 36 bytes in 1 blocks are still reachable in loss record 2 of 4 
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400B7EC: _dl_new_object (dl-object.c:161) 
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051) 
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== 
==27802== 312 bytes in 1 blocks are still reachable in loss record 3 of 4 
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x4010B59: _dl_check_map_versions (dl-version.c:300) 
==27802== by 0x4013E1F: dl_open_worker (dl-open.c:268) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130) 
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265) 
==27802== 
==27802== 1,174 bytes in 1 blocks are still reachable in loss record 4 of 4 
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400B57D: _dl_new_object (dl-object.c:77) 
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051) 
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== 
==27802== LEAK SUMMARY: 
==27802== definitely lost: 0 bytes in 0 blocks 
==27802== indirectly lost: 0 bytes in 0 blocks 
==27802==  possibly lost: 0 bytes in 0 blocks 
==27802== still reachable: 1,558 bytes in 4 blocks 
==27802==   suppressed: 0 bytes in 0 blocks 
==27802== 
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
--27802-- 
--27802-- used_suppression:  2 dl-hack3-cond-1 
==27802== 
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 

コードスニペット用Valgrindの出力用

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 1 extra malloc vs free (most frequently) 
    Most surprisingly, it seems there is a random amount of overall mallocs 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* For the sake of returning something, not necessary */ 
    return NULL; 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread detached attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Delay main() completion until all detached threads finish their jobs. */ 
    usleep(100000); 
    return EXIT_SUCCESS; 
} 

Valgrindの出力:

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 1 extra malloc vs free (most frequently) 
    Most surprisingly, it seems there is a random amount of overall mallocs 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 
#include <unistd.h>   

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* For the sake of returning something, not necessary */ 
    return NULL; 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread joinable attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
     /* Detach worker after creation */ 
     pthread_detach(workers[tid]); 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Delay main() completion until all detached threads finish their jobs. */ 
    usleep(100000); 
    return EXIT_SUCCESS; 
} 

コード3(作成時に取り外さスレッド)スニペット1(mem-leakなし、後で数回実行)

--29170-- Discarding syms at 0x64168d0-0x6426198 in /lib/x86_64-linux-gnu/libgcc_s.so.1 due to munmap() 
==29170== 
==29170== HEAP SUMMARY: 
==29170==  in use at exit: 0 bytes in 0 blocks 
==29170== total heap usage: 105 allocs, 105 frees, 28,814 bytes allocated 
==29170== 
==29170== All heap blocks were freed -- no leaks are possible 
==29170== 
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
--29170-- 
--29170-- used_suppression:  2 dl-hack3-cond-1 
==29170== 
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
+0

valgrindの出力は何ですか? (別名:そのコードのどこにでも 'pthread_exit'を使う必要はありません。ちょうど' 0を返します。 ') –

+0

返り値0を返します。 main()のpthread_exit()の代わりに。また、joined()コードスニペットのvalgrind出力を追加し、main()の完了時間を2番目と3番目の質問をクリアしました。 – swappy

+0

これは本当にメモリリークではなく、すべてのメモリにはまだ到達可能です。 –

答えて

5

スレッドが切り離されたときに不具合が発生し、未定義の動作が発生します。メインで

あなたはこのコード行があります:あなたのワーカースレッドへのポインタの

struct args_for_job_t args[MAX_THREADS]; 

あなたが手を。

そして、メイン()この部分は

pthread_exit(NULL); 

そしてメイン達する())が存在しなくなっていますが、まだ周りのワーカースレッドを有していてもよく、(メインのスタック上にある上記args配列にアクセスしています - もう存在しない。 一部の実行でmain()が終了する前にワーカースレッドがすべて終了する可能性がありますが、他の実行では終了しない可能性があります。

+2

ありがとうございました。私はこのような動作を疑っていましたが、二重チェックしたいと思いました。私はまた、pthread_exit(NULL)の前にタイマー(usleep())を追加しようとしました(またはJonathanが提案したように0を返します)。私は、returnの代わりにpthread_exit()を使用すると、すべてのワーカーが完了するまで(まさにpthread_join()が実行する)main()スレッドにハングするように指示するという印象を受けていました。 – swappy

+0

私は修正した、私は分離されたスレッドで10000から100000までのスリープ時間をバンプし、すべてのメモリリークがなくなったようだ、これはメイン()が終了する前にすべてのスレッドを完了するのに十分な時間を残す。 – swappy

+0

申し訳ありませんが、 'main'の' pthread_exit'が待ち状態になるのは間違いありません。他のスレッドとスニペット1の 'pthread_exit'への呼び出しは冗長です –

関連する問題