Posixスレッドプログラミングガイド(4)スレッド終了


スレッド終端方式
一般に,Posixのスレッド終端には,通常終端と非正常終端の2つのケースがある.スレッドアクティブ呼び出しpthread_exit()またはスレッド関数からreturnは、スレッドを正常に終了させます.これは、予測可能な終了方法です.非正常な終了は、スレッドが他のスレッドの介入の下で、または不正なアドレスへのアクセスなどの自身の実行エラーによって終了することであり、このような終了方式は予見できない.
 
トップに戻る
スレッド終了時のクリーンアップ
予測可能なスレッド終了でも異常終了でも、リソース解放の問題があり、実行エラーによる終了を考慮せずにスレッド終了時に自分の占有するリソース、特にロックリソースを順調に解放できることをどのように保証するかは、解決しなければならない問題である.
最も頻繁に発生する状況は、リソース排他ロックの使用です.スレッドは、臨界リソースにアクセスするためにロックされていますが、アクセス中に外部によってキャンセルされ、スレッドが応答キャンセル状態にあり、非同期で応答している場合、または排他ロックを開く前の運転経路にキャンセルポイントがある場合、臨界リソースは常にロック状態で解放されません.外部キャンセル操作は予見できないため、リソース解放のためのプログラミングを簡略化するメカニズムが必要である.
POSIXスレッドAPIにはpthread_が用意されているcleanup_push()/pthread_cleanup_pop()関数ペアは、リソースを自動的に解放するために使用されます.pthread_からcleanup_push()の呼び出しポイントからpthread_cleanup_pop()間のプログラムセグメントでの終了動作(pthread_exit()の呼び出しとキャンセルポイントの終了を含む)は、pthread_を実行します.cleanup_push()で指定したクリーンアップ関数.APIは以下のように定義される.
void pthread_cleanup_push(void (*routine) (void  *),  void *arg)

void pthread_cleanup_pop(int execute)


 
pthread_cleanup_push()/pthread_cleanup_pop()は先入後出のスタック構造管理を採用し、void routine(void*arg)関数はpthread_を呼び出すcleanup_push()時にクリーンアップ関数スタックに圧入し、pthread_を複数回cleanup_push()の呼び出しは、関数スタックをクリーンアップし、その関数チェーンを実行するときにスタックの反対の順序でポップアップする関数チェーンを形成します.executeパラメータはpthread_に実行されたことを示します.cleanup_pop()時にクリーンアップ関数をポップアップしながら実行するかどうかは、0は実行しないことを示し、0は実行ではない.このパラメータは、異常終了時のクリーンアップ関数の実行に影響しません.
pthread_cleanup_push()/pthread_cleanup_pop()は、pthread.hのマクロ定義であるマクロで実現される.
#define pthread_cleanup_push(routine,arg)                                     \

  { struct _pthread_cleanup_buffer _buffer;                                   \

    _pthread_cleanup_push (&_buffer, (routine), (arg));

#define pthread_cleanup_pop(execute)                                          \

    _pthread_cleanup_pop (&_buffer, (execute)); }


 
表示、pthread_cleanup_push()には「{」があり、pthread_cleanup_pop()には「}」があるため、この2つの関数はペアで表示され、コンパイルを通過するにはプログラムの同じレベルのコードセグメントに存在する必要があります.次の例では、スレッドが「do some work」で終了するとpthread_がアクティブに呼び出されます.mutex_unlock(mut)は、ロック解除動作を完了します.
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);

pthread_mutex_lock(&mut);

/* do some work */

pthread_mutex_unlock(&mut);

pthread_cleanup_pop(0);


 
スレッドがPTHREAD_にある場合はCANCEL_ASYNCRHRONOUSステータスでは、CANCELイベントがpthread_にある可能性があるため、上記のコードセグメントでエラーが発生する可能性があります.cleanup_push()とpthread_mutex_lock()間で発生するか、pthread_mutex_unlock()とpthread_cleanup_pop()の間で発生し、関数unlockがロックされていないmutex変数をクリーンアップし、エラーが発生します.したがって、クリーンアップ関数を使用する場合は、PTHREAD_に一時的に設定する必要があります.CANCEL_DEFERREDモード.このため、POSIXのLinux実装では、移植が保証されていないpthreadのペアも提供される.cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()拡張関数は、次のコードセグメントに相当します.
{ int oldtype;

 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);

 pthread_cleanup_push(routine, arg);

 ...

 pthread_cleanup_pop(execute);

 pthread_setcanceltype(oldtype, NULL);

 }

 
 
トップに戻る
スレッド終了の同期とその戻り値
一般的に、プロセス内の各スレッドの実行は互いに独立しており、スレッドの終了は通知されず、他のスレッドにも影響を与えず、終了したスレッドが占有するリソースもスレッドの終了に伴って解放されません.プロセス間でwait()システム呼び出しを使用してリソースを同期的に終了して解放できるように、スレッド間にもpthread_という同様のメカニズムがあります.join()関数.
void pthread_exit(void *retval) 

int pthread_join(pthread_t th, void **thread_return)

int pthread_detach(pthread_t th)


 
pthread_join()の呼び出し者はthスレッドの終了を保留し、retvalはpthread_である.exit()呼び出しスレッド(スレッドID th)の戻り値、thread_returnがNULLでない場合*thread_return=retval.スレッドはpthread_を使用する唯一のスレッドのみを許可することに注意してください.join()はその終了を待機し、待機しているスレッドはjoin可能な状態、すなわち非DETACHED状態にあるべきである.
プロセス内のスレッドがpthread_を実行した場合detach(th)の場合、thスレッドはDETACHED状態になります.これにより、thスレッドは実行終了時に使用したメモリリソースを自動的に解放し、pthread_join()同期、pthread_detach()実行後、thリクエストpthread_join()はエラーを返します.
join可能なスレッドが消費するメモリは、スレッドがpthread_を実行した場合にのみ使用されます.join()後にリリースされるため、メモリの漏洩を避けるために、すべてのスレッドの終了はDETACHEDに設定されているか、pthread_を使用する必要があります.join()で回収します.
 
トップに戻る
pthread_について义齿
理論的にはpthread_exit()はスレッドシンク関数が終了する機能と同じで、関数が終了するとpthread_が内部で自動的に呼び出されます.exit()は、スレッドに関連するリソースをクリーンアップします.しかし、実際には両者はコンパイラの処理によって大きく異なる.
プロセスメイン関数(main()でpthread_を呼び出すexit()は、メイン関数が存在するスレッド(プロセスのメインスレッドと言える)のみを終了させる.一方、returnの場合、コンパイラは、_exit()などのプロセスが終了したコードを呼び出し、プロセスとそのすべてのスレッドが実行を終了します.
次に、スレッドホスト関数でreturnをアクティブに呼び出し、return文がpthread_に含まれている場合cleanup_push()/pthread_cleanup_pop()ペアでは、クリーンアップ関数の実行は起こらず、逆にsegment faultになります.