UNIX/Linux-スレッド制御(インスタンス入門編)


UNIXスレッド制御
スレッドのプロパティ
スレッドを作成するときにpthread_を使用できます.attr_t構造は、スレッドのデフォルト属性を変更し、作成したスレッドに関連付けます.pthread_を使用できますattr_init関数初期化pthread_attr_t構造.
#include
int  pthread_attr_init (pthread_attr_t* attr) ;
int  pthread_attr_destroy (pthread_attr_t* attr) ;
スレッド属性には、スレッドの分離状態属性、スレッドスタックの末尾の警戒バッファサイズ、スレッドスタックの最下位アドレス、スレッドスタックのサイズ......
 
スレッドの分離
既存のスレッドの終了状態に興味がない場合はpthread_を使用します.detach関数は、スレッドが終了したときにオペレーティングシステムが使用するリソースを回収します.(分離:他のスレッドに連絡しない、すなわち自分の終了状態をスレッドに返さない)
スレッドが分離されている場合はpthread_join呼び出しは失敗し、EINVALに戻ります.
スレッドの作成時にスレッドの終了状態を理解する必要がないことを知っている場合は、pthread_を変更できます.attr_t構造の関連属性、pthread_を使用できますattr_setdetachstate関数スレッド属性detachstateを:PTHREAD_に設定CREATE_DETACHEDは、分離状態で起動します.またはPTHREAD_に設定CREATE_JOINABLE、スレッドを正常に起動すると、アプリケーションはスレッドの終了状態を取得できます.
 
スレッドプライベートデータ
オンライン・スレッド内のグローバル変数が必要な場合があります.つまり、この変数はこのスレッドでのみ使用でき、他のスレッドはアクセスできません.(スレッドは複数のモジュールを呼び出し、各モジュール間にグローバル変数が必要になる場合があります)
(もちろん、スレッド関数で変数を定義し、そのアドレスを各サブモジュール関数に渡すこともできますが、これは不便で、関数のインタフェースを複雑にすることができます)
 
スレッドのプライベートデータを割り当てる前に、そのデータに関連付けられたキーを作成する必要があります.このキーは、スレッドのプライベートデータへのアクセス権を取得するために使用されます.
#include
int  pthread_key_create(pthread_key_t * keyp, void (*destructor)(void *)) ;
作成したキーはkeypが指すメモリセルに格納され、作成キー以外はpthread_key_createは、スレッドがpthread_を呼び出すと、キー関連解析関数として選択できます.exitまたはスレッドは戻りを実行し、正常に終了すると、構造関数が呼び出されます.スレッドがexit,_を呼び出した場合exit、_Exit、abort、または他の異常な終了が発生した場合、構造関数は呼び出されません.
スレッドは通常mallocを使用してスレッドのプライベートデータにメモリ領域を割り当て、構造関数は通常割り当てられたメモリを解放します.
 
キーを作成すると、pthread_を呼び出すことができます.setspecific関数は、キーとスレッドのプライベートデータを関連付けます.pthread_でgetspecific関数は、スレッドのプライベートデータのアドレスを取得します.
#include
int  pthread_setspecific(pthread_key_t key,  const void * value) ;
void*  pthread_getspecific(pthread_key_t key) ;
スレッドのプライベートデータ値がキーに関連付けられていない場合、pthread_getspecificは空のポインタを返し、pthread_を呼び出す必要があるかどうかを決定します.setspecific
 
【例】
//         
//
//
#include 
#include 
#include 
#include  

//                  (               )
static pthread_key_t key ; 

//                
struct
{
    pthread_cond_t cond ;
    pthread_mutex_t mutex ;
    int nready ;        //        
} condForThread1 = {
    PTHREAD_COND_INITIALIZER ,
    PTHREAD_MUTEX_INITIALIZER
} ;

struct
{
    pthread_cond_t cond ;
    pthread_mutex_t mutex ;
    int nready ;        //        
} condForThread2 = {
    PTHREAD_COND_INITIALIZER ,
    PTHREAD_MUTEX_INITIALIZER
} ;

void* thread1(void* arg);
void* thread2(void* arg);
void childfunc();

int 
main(void)
{
    pthread_t tid1, tid2 ;
    
    pthread_create(&tid1, NULL, thread1, NULL) ;
    pthread_create(&tid2, NULL, thread2, NULL) ;

    pthread_join(tid1, NULL) ;
    pthread_join(tid2, NULL) ;

    return 0 ;
}

void*
thread1(void* arg)
{
    char* privateBuf = NULL ;
    int   bufsize = 10 ;
    int   i = 0 ;

    pthread_key_create(&key, free) ; //   

    //           (             )
    privateBuf = (char*)pthread_getspecific(key) ; 
    if (privateBuf == NULL)
    {
        privateBuf = malloc(bufsize) ;
        if (privateBuf == NULL)
            return ;
    }
    //          
    pthread_setspecific(key, privateBuf) ;

    childfunc() ; //            

    //  buf
    for (i = 0; i < bufsize; ++i)
    {
        printf("%d,
", privateBuf[i]) ; } // 2 2 condForThread2.nready = 1 ; pthread_cond_signal(&condForThread2.cond) ; // 1 pthread_mutex_lock(&condForThread1.mutex) ; pthread_cond_wait(&condForThread1.cond, &condForThread1.mutex) ; pthread_mutex_unlock(&condForThread1.mutex) ; } void childfunc() { int i = 0 ; char* buf ; buf = (char*)pthread_getspecific(key) ; for (i = 0; i < 10; ++i) { buf[i] = i ; } } void* thread2(void* arg) { char* privateBuf = NULL ; int bufsize = 10 ; int i = 0 ; // 1 pthread_mutex_lock(&condForThread2.mutex) ; pthread_cond_wait(&condForThread2.cond, &condForThread2.mutex) ; pthread_mutex_unlock(&condForThread2.mutex) ; // ( , key) privateBuf = (char*)pthread_getspecific(key) ; // buf for (i = 0; i < bufsize; ++i) { printf("*%d,
", privateBuf[i]) ; } // 1 condForThread1.nready = 1 ; pthread_cond_signal(&condForThread1.cond) ; }

 
スレッドと信号
プロセス中の信号は単一のスレッドに送られ、ハードウェア障害またはタイマタイムアウトに関連する信号がある場合、イベントを引き起こすスレッドに送信され、他の信号は任意のスレッドに送信される.
 
各スレッドには独自の信号シールドワードがありますが、信号の処理はプロセス内のすべてのスレッドで共有されます.
 
マルチスレッド環境での信号マスクの設定:pthread_の使用Sigmask関数(sigprocmaskの動作はマルチスレッドのプロセスで定義されていないため)
#include
int  pthread_sigmask (int how,  const sigset_t * set,  sigset_t* oset) ;
この関数はsigprocmask関数とほぼ同じです.
 
スレッド待機信号
#include
int  sigwait (const sigset_t * set,  int * signop) ;
パラメータsetはスレッドが待つ信号セットを指摘し、パラメータsignopは戻り値がどの信号を待つかを示す.
注意:
エラー動作を回避するために、スレッドはsigwaitを呼び出す前に、待機している信号をブロックしなければならない.Sigwait関数は、新しい信号が送信されるまで、信号セットのブロック状態を自動的にキャンセルします.戻る前にsigwaitはスレッドの信号シールドワードを復元します.
 
sigwaitを用いる利点は、信号処理を簡略化できることである.
信号がスレッドを中断することを防止するために、各スレッドの信号シールドワードに信号を加算し、専用スレッドを信号処理に手配することができる.sigwaitは信号のブロック状態を解除するので、信号の受信に使用できるスレッドは1つしかありません.これにより、プライマリスレッドを符号化する際に、これらの信号からの割り込みを心配する必要がなくなる.
【注意】アラームタイマはプロセスリソースであり、すべてのスレッドが同じalarmを共有する.したがって、プロセス内の複数のスレッドが互いに干渉せずにアラームタイマを使用することはできない.
 
【例】
//        
//SIGQUIT      ctrl+\
//SIGINT       ctrl+c
#include 
#include 
#include 
#include 
#include  

sigset_t mask ; //   

int  quitflag ; //         
pthread_cond_t waitcond = PTHREAD_COND_INITIALIZER ;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER ;

void* thr_signal_handle(void* arg) ;

int 
main(void)
{
    int       err ;
    sigset_t  oldmask ;
    pthread_t tid ;
    
    sigemptyset(&mask) ;
    sigaddset(&mask, SIGINT) ;
    sigaddset(&mask, SIGQUIT) ;

    //         
    if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
        perror("SIG_BLOCK error!
") ; // if ((err = pthread_create(&tid, NULL, thr_signal_handle, NULL)) != 0) perror("can't create thread!
") ; // pthread_mutex_lock(&lock) ; while (quitflag == 0) pthread_cond_wait(&waitcond, &lock) ; pthread_mutex_unlock(&lock) ; quitflag = 0 ; // if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) perror("SIG_SETMASK error!
") ; exit(0) ; } // void* thr_signal_handle(void* arg) { int err, signo ; for(;;) { if ((err = sigwait(&mask, &signo)) != 0) perror("sigwait error!
") ; switch(signo) { case SIGINT: printf("
interrupt
") ; break ; case SIGQUIT: // pthread_mutex_lock(&lock) ; quitflag = 1 ; pthread_mutex_unlock(&lock) ; pthread_cond_signal(&waitcond) ; return ; default: printf("unexpected signal %d
", signo) ; exit(1) ; } }//for(;;) }

 
スレッドに信号を送る
プロセスに信号を送信するにはkillを呼び出すことができます.スレッドに信号を送信するにはpthread_を呼び出すことができます.kill.
#include
int  pthread_kill (pthread_t tid, int signo) ;
 
【例】
//        
#include 
#include 
#include 
#include 
#include  

sigset_t mask ; //   
pthread_t tidbuf[5] ; //          ID

int  flag ; 
pthread_cond_t waitcond = PTHREAD_COND_INITIALIZER ;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER ;

void* thr1(void* arg) ;
void* thr2(void* arg) ;

int 
main(void)
{
    int       err ;
    sigset_t  oldmask ;
    pthread_t tid ;
    
    sigemptyset(&mask) ;
    sigaddset(&mask, SIGUSR1) ;

    //        
    if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0)
        perror("SIG_BLOCK error!
") ; // if ((err = pthread_create(&tidbuf[0], NULL, thr1, NULL)) != 0) perror("can't create thread!
") ; if ((err = pthread_create(&tidbuf[1], NULL, thr2, NULL)) != 0) perror("can't create thread!
") ; // pthread_join(tidbuf[0], NULL) ; pthread_join(tidbuf[1], NULL) ; exit(0) ; } void* thr1(void* arg) { int err, signo ; // 2 pthread_mutex_lock(&lock) ; while(flag == 0) pthread_cond_wait(&waitcond, &lock) ; pthread_mutex_unlock(&lock) ; // 2 if ((err = sigwait(&mask, &signo)) != 0) perror("sigwait error!
") ; if (signo == SIGUSR1) printf("
I catched the signal from thread2!
") ; } // void* thr2(void* arg) { int err, signo ; // 1 pthread_mutex_lock(&lock) ; flag = 1 ; pthread_mutex_unlock(&lock) ; pthread_cond_signal(&waitcond) ; pthread_kill(tidbuf[0], SIGUSR1) ; }

【注意】多くのlinuxシステムはコンパイル時にスレッドライブラリを自動的にリンクしないため、コンパイル時にリンクを指定する必要がある:例gcc a.c-l pthread