web-dev-qa-db-ja.com

pthreadがメモリリークを引き起こす理由

Pthreadを作成するたびに、valgrindがメモリリークを出力し、

たとえば、以下のコード:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h> 

void *timer1_function (void *eit){
  (void) eit;
    printf("hello world\n");
    pthread_exit(NULL);
}

int main(void){
   pthread_t timer1;
   pthread_create( &timer1, NULL, timer1_function,  NULL);  ///////line13
   int i=0;
   for(i=0;i<2;i++){usleep(1);}
   return 0;
}

valgrind出力

==1395== HEAP SUMMARY:
==1395==     in use at exit: 136 bytes in 1 blocks
==1395==   total heap usage: 6 allocs, 5 frees, 1,134 bytes allocated
==1395== 
==1395== 136 bytes in 1 blocks are possibly lost in loss record 1 of 1
==1395==    at 0x402A629: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==1395==    by 0x4011304: allocate_dtv (dl-tls.c:297)
==1395==    by 0x4011AAB: _dl_allocate_tls (dl-tls.c:461)
==1395==    by 0x4052470: pthread_create@@GLIBC_2.1 (allocatestack.c:571)
==1395==    by 0x8048566: main (test.c:13)
==1395== 
==1395== LEAK SUMMARY:
==1395==    definitely lost: 0 bytes in 0 blocks
==1395==    indirectly lost: 0 bytes in 0 blocks
==1395==      possibly lost: 136 bytes in 1 blocks
==1395==    still reachable: 0 bytes in 0 blocks
==1395==         suppressed: 0 bytes in 0 blocks

manページをリファレンスとして使用していたのに、なぜpthread_createが問題を引き起こし、どうすれば修正できますか?

21
Johan Elmander

スレッドは割り当てられたリソースであり、終了する前に解放していません。 pthread_joinを呼び出す必要があります。これにより、ハックや不適切なスリープループが不要になります。

POSIXスレッドのいくつかの実装(glibc/NPTLを使用していると思います)がキャッシュを完全に解放するのではなく、スレッドリソースを再利用するため、これを修正しても、valgrindは依然として「リーク」を見る可能性があります。 valgrindがこれを回避するかどうかはわかりません。

valgrindは、終了時のプログラムの状態を分析していると思います。これは、スレッドの実行が完了する前の可能性があります。2マイクロ秒では"Hello, world!\n"をコンソールに書き込むのに十分ではない可能性があります。 pthread_join への呼び出しを追加すると、このリークが修正されます:

pthread_join(timer1, NULL);
5
dasblinkenlight

表示されるリークは、子スレッドのローカルストレージ(tls)に割り当てられているDTV(動的スレッドベクトル)構造に関連しています。

メインスレッド(つまり、子を生成したスレッド)でpthread_join()を使用すると、リークを確実に修正できます。 pthread_join()呼び出しが不要なユースケースでは、子pthread_tを指定して_pthread_detach_を呼び出すと、メモリが確実に解放されます。

_pthread_detach_の男性から:

pthread_detach()関数は、threadによって識別されたスレッドを切り離されたものとしてマークします。切り離されたスレッドが終了すると、そのリソースは自動的にシステムに解放され、別のスレッドが終了したスレッドに参加する必要はありません。

1
daemon155

Pthread_joinの呼び出しに失敗したときも、同様の結果が出ました。

Pthread_joinを呼び出しても、Valgrindはメモリエラーやリークを示しません。 pthread_killを使用してスレッドがまだ存在するかどうかを確認した後、joinを呼び出してリソースをクリーンアップして解放しました。

int
stop_worker(worker_t *self)
{
    if (self) {
        // signal the thread to quit
            // (here using a variable and semaphore)
        self->thread_quit=TRUE;
        sem_post(&self->sem);

        // wait for it to stop
        // (could use counter, etc. to limit wait)
        int test=0;
        while (pthread_kill(self->thread,0) == 0) {
            MDEBUG(MD_XF_LOGGER,"waiting for thread to exit...\n",test);
            delay_msec(50);
        }

        // even though thread is finished, need to call join
        // otherwise, it will not release its memory (and valgrind indicates a leak)
        test=pthread_join(self->thread,NULL);
        return 0;           
    }
    return -1;
}
1
klheadley

メモリリークは、キャンセルせずにスレッドを実行したままにすると、対応する動的に割り当てられたメモリが解放されないために発生します。 pthread_cleanup_Push(CleanupHandler、NULL)およびpthread_cleanup_pop(0)とともにpthread_cancel()を使用して、キャンセル後にスレッドのクリーンアップを実行します。

0
wrapperm