以下のように定義された参加可能なpthreadランナー関数があります。
void *sumOfProducts(void *param)
{
...
pthread_exit(0);
}
このスレッドはメインスレッドに参加することになっています。
Valgrindを介してプログラムを実行すると、常に以下のリークが表示されます。
LEAK SUMMARY:
definitely lost: 0 bytes in 0 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 968 bytes in 5 blocks
suppressed: 0 bytes in 0 blocks
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 10)
私は言ったpthreadsのmanページをチェックしました:
The new thread terminates in one of the following ways:
* It calls pthread_exit(3), specifying an exit status value that is
available to another thread in the same process that calls
pthread_join(3).
* It returns from start_routine(). This is equivalent to calling
pthread_exit(3) with the value supplied in the return statement.
* It is canceled (see pthread_cancel(3)).
* Any of the threads in the process calls exit(3), or the main thread
performs a return from main(). This causes the termination of all
threads in the process.
奇妙なことに、pthread_exit()をreturnステートメントに置き換えたところ、リークが消えました。
return(NULL);
私の実際の質問は3つの方向に分かれています:
次の最小限のテストケースは、説明した動作を示しています。
_#include <pthread.h>
#include <unistd.h>
void *app1(void *x)
{
sleep(1);
pthread_exit(0);
}
int main()
{
pthread_t t1;
pthread_create(&t1, NULL, app1, NULL);
pthread_join(t1, NULL);
return 0;
}
_
_valgrind --leak-check=full --show-reachable=yes
_は、解放されていないが、プロセスの終了時に到達可能なpthread_exit()
によって呼び出された関数から割り当てられた5つのブロックを示しています。 pthread_exit(0);
が_return 0;
_に置き換えられた場合、5つのブロックは割り当てられません。
ただし、多数のスレッドの作成と結合をテストすると、終了時に使用されている未解放のメモリの量がnot増加することがわかります。これと、それがまだ到達可能であるという事実は、glibc実装の奇妙さだけを見ていることを示しています。いくつかのglibc関数は、最初に呼び出されたときにmalloc()
を使用してメモリを割り当てます。これらの関数は、プロセスの存続期間の残りの間割り当てられ続けます。 glibcはプロセスの終了時にこのメモリを解放する必要はありません。プロセスがとにかく取り壊されていることを知っているからです。CPUサイクルの無駄になるだけです。
まだこれに興味があるかどうかはわかりませんが、現在、同様の状況をデバッグしています。 pthread_exit
を使用するスレッドは、valgrindに到達可能なブロックを報告させます。その理由はここでかなりよく説明されているようです:
https://bugzilla.redhat.com/show_bug.cgi?id=483821
基本的に、pthread_exit
はdlopen
を引き起こしているようです。これは、プロセスの終了時に明示的にクリーンアップされることはありません。
これは、exit()(および、明らかに、pthread_exit())を呼び出すと、自動的に割り当てられた変数が割り当てられたままになります。正しく巻き戻すには、戻るか投げる必要があります。
@Klaim:その文書が私が間違っていると言っているところはわかりませんが、もしそうならそれは間違っています。 C++標準(§18.3/ 8)を引用するには、「exit()を呼び出しても自動オブジェクトは破棄されません。」 – James McNellis、10月10日19:11
"pthread_exit(0)"の代わりに "return 0"を実行することで問題が解決されたように見えるので(そして私のおかげで)、2つの動作は似ていると想定しています。
Valgrindは、結合可能なスレッドの状態に割り当てられているストレージを追跡するのが難しいという経験があります。 (これはcafが示すのと同じ方向になります。)
常に0
の値を返すようですので、おそらくアプリケーションの観点からスレッドに参加する必要があると思いますか?その場合、最初から切り離してそれらを起動することを検討すると、そのメモリの割り当てが回避されます。
欠点は、次のいずれかです。
main
の最後に独自のバリアを実装します。事前にスレッド数がわかっている場合は、静的に割り当てられた単純なpthread_barrier
で十分です。main
をpthread_exit
で終了して、まだ完了していない可能性がある残りの実行中のスレッドを強制終了しないようにします。あなたは実際にC++を使用していますか?明確にするために、ソースファイルは.c
拡張子で終了し、g++
ではなくgcc
でコンパイルしていますか?
関数が戻ったときに自動的にクリーンアップされると予想されるリソースを関数が割り当てている可能性はかなり高いようです。 std::vector
やstd::string
のようなローカルC++オブジェクトはこれを行い、pthread_exit
を呼び出した場合、それらのデストラクタはおそらく実行されませんが、戻っただけではクリーンアップされます。
私の好みは、pthread_exit
などの低レベルAPIを避け、可能な場合は常にスレッド関数から戻るだけです。これらは同等ですが、pthread_exit
は、使用している言語をバイパスする事実上のフロー制御構成ですが、return
はそうではありません。