Linux Cスレッド制御(二)

9077 ワード

四、私有データ
  • マルチスレッド環境では、プロセス内のすべてのスレッドがプロセスのデータ空間を共有するため、グローバル変数はすべてのスレッドに共有されます.プログラム設計では、スレッド自体のグローバル変数を保存する必要がある場合があります.この特殊な変数は、あるスレッド内部でのみ有効です.スレッド内部では、スレッドプライベートデータは各関数によってアクセスできますが、他のスレッドに対してはマスクされています.スレッドプライベートデータは、1つのキーが複数の数値に対応する1つのキー多値と呼ばれる技術を採用している.データにアクセスするときはキー値でアクセスしますが、1つの変数にアクセスするようですが、実は異なるデータにアクセスしています.スレッドプライベートデータを使用する場合は、まず各スレッドデータに関連付けられたキーを作成します.各スレッドの内部では、この共通のキーを使用してスレッドデータを指すが、異なるスレッドでは、このキーが表すデータは異なる.操作スレッドのプライベートデータの関数は主に4つあり、それぞれ作成、設定、読み取り、削除:
  • #include 
    int pthread_key_create(pthread_key_t *key,void(*destr_function)(void*));
    int pthread_setspecific(pthread_key_t key,const (void* pointer));
    void* pthread_getspecific(pthread_key_t key);
    int pthread_key_delete(pthread_key_t key);
  • pthread_key_create:LinuxdのTSDプールから1つの値を割り当て、後でアクセスするためにkeyに値を割り当てます.彼の最初のパラメータkeyはキー値を指すポインタで、2番目のパラメータは関数ポインタで、ポインタが空でない場合、スレッド終了時にkeyに関連付けられたデータをパラメータとしてdestr_を呼び出します.function()は、割り当てられたバッファを解放します.keyが作成されると、すべてのスレッドが彼にアクセスできますが、各スレッドは自分の必要に応じてkeyに異なる値を入力することができます.これは、同じ名前で異なる値を提供するグローバル変数に相当します.重要なデータ構造配列、すなわちTSDプールに依存し、構造は以下の通りである:
  • static struct pthread_key_struct pthread_keys [PTHREAD_KEYS_MAX] = { { 0, NULL } };

    TSDを作成することは、構造配列のいずれかを「in_use」に設定し、*keyにインデックスを返し、destructor関数をdestr_に設定することに相当します.function.
  • pthread_setspecific:pointerの値(コンテンツではない)をkeyに関連付けます.pthread_でsetspecificがキーに新しいスレッドデータを指定する場合、スレッドはまず既存のスレッドデータを解放して空間を回収する必要があります.
  • pthread_getspecific:keyに関連付けられたデータをこの関数で取得します.
  • pthread_key_delete:この関数はキーを削除するために使用され、削除するとキーに使用されるメモリが解放されます.なお、キーが占有するメモリは解放され、キーに関連付けられたスレッドデータが占有するメモリは解放されない.したがって、スレッドデータの解放は、キーを解放する前に完了する必要があります.

  • スレッドを使用したプライベートデータを作成するには、次の手順に従います.
    #include 
    #include 
    #include 
    #include 
    pthread_key_t key;
    
    void * thread2(void*arg)
    {
    	int tsd = 5;
    	printf("thread %ld is running
    ",pthread_self()); pthread_setspecific(key, (void *) tsd); printf("thread %ld returns %p
    ",pthread_self(),pthread_getspecific(key)); } void * thread1(void *arg) { int tsd = 0; pthread_t thid2; printf("thread %ld is running
    ",pthread_self()); pthread_setspecific(key, (void *) tsd); pthread_create(&thid2,NULL,thread2,NULL); sleep(5); printf("thread %ld returns %p
    ",pthread_self(),pthread_getspecific(key)); } int main() { pthread_t thid1; printf("main thread begins running
    "); pthread_key_create(&key,NULL); pthread_create(&thid1,NULL,thread1,NULL); sleep(3); pthread_key_delete(key); printf("main thread exit
    "); return 0; }

    実行結果は次のとおりです.
    main thread begins running
    thread 140601797752576 is running
    thread 140601789359872 is running
    thread 140601789359872 returns 0x5
    main thread exit
    

    五、スレッド同期
  • 反発ロック
  • ロックメカニズムによりスレッド間の同期を実現する.同じ時点で1つのスレッドのみがキー部分コードを実行できるようにします.
    反発ロック関数
    手紙の数
    こうりょく
                pthread_mutex_init
    反発ロックの初期化
              pthread_mutex_destroy
    反発ロックのログアウト
               pthread_mutex_lock
    ロック、失敗した場合はブロック待ち
               pthread_mutex_unlock
    ロック解除
              pthread_mutex_trylock
    ロックをテストし、失敗した場合はiEBUSYに戻ります.
    反発ロックを使用する前に、先進的な初期化操作が必要です.初期化には2つの方法があり、1つは静的付与法であり、マクロ構造定数PTHREAD_をMUTEX_INITIALIZERは反発ロックを与え、操作文は以下の通りである.
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    もう1つの方法はpthread_を介してmutex_Init関数は反発ロックを初期化し、この関数の原型は以下の通りである.
    int pthread_mutex_init (pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);

    関数のパラメータmutexattrは反発ロックのプロパティを表し、NULLの場合はデフォルトのプロパティを使用します.反発ロックの属性と意味:
    反発ロックのプロパティ
    属性値
     
    PTHREAD_MUTEX_TIMED_NP
    ≪一般ロック|General Locks|emdw≫:1つのスレッドがロックされると、残りのリクエストのスレッドが待機キューを形成し、ロックが解除されると優先度でロックが取得されます.
    PTHREAD_MUTEX_RECURSIVE_NP
    ネストされたロック:同じロックに1つのスレッドが複数回ロックされ、複数回unlockでロックが解除されます.異なるスレッドリクエストの場合は、ロック解除時に再競合します.
    PTHREAD_MUTEX_ERRORCHECK_NP
    エラーチェックロック:同じスレッドが同じロックを要求した場合、EDEADLKに戻ります.そうでない場合、実行されるアクションはタイプPTHREAD_となります.MUTEX_TIMED_NP同一
    PTHREAD_MUTEX_ADAPTIVE_NP
    適応ロック:ロック解除後の再競合
    2つのロック関数:
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);

    pthread_でmutex_lock()ロック時、mutexがロックされている場合、現在ロックを試みているスレッドは、反発ロックが他のスレッドによって解放されるまでブロックされます.pthread_mutex_lock()関数が返されると、反発ロックが現在のスレッドによって正常にロックされていることを示します.pthread_mutex_trylock関数は異なり、mutexがロックされている場合、ブロック待ちではなくebusyが返されます.
    ロックを追加する場合、そのタイプのロックにかかわらず、2つの異なるスレッドによって同時に取得されることはできません.そのうちの1つはロック解除を待たなければなりません.同じプロセスのスレッドで、ロックが解除されていない場合、他のスレッドはロックを取得できません.
    関数pthread_mutex_unlockはロックを解除するために使用されます.
    int pthread_mutex_unlock(pthread_mutex_t *mutex)

    ロックを解除するときは、2つの条件を満たす必要があります.1つは、反発ロックがロックされていること、2つは、この関数を呼び出すスレッドが反発ロックされているスレッドであることです.ロック解除後、他のスレッドが反発ロックを待っている場合、待機キューの最初のスレッドは反発ロックを取得します.
    反発ロックが使用されると、クリアする必要があります.反発ロックのクリア使用関数pthread_mutex_destroy、この関数のプロトタイプは次のとおりです.
    int pthread_mutex_destroy(pthread_mutex_t *mutex);

    反発ロックをクリアすることは、彼が占有しているリソースを解放することを意味します.ロックをクリアするには現在オープン状態であることが要求され、ロックがロック状態である場合、関数はEBUSYに戻り、この関数が正常に実行されると0に戻る.Linuxでは反発ロックはメモリを消費しないため、この関数は反発ロックの状態に解除される以外に動作しない.
  • 条件変数
  • 条件変数は,スレッド間で共有されるグローバル変数を用いて同期するメカニズムである.条件変数はマクロ的にif文に似ており、条件を満たすとプログラムを実行できます.そうしないと、条件が成立するのを待つしかありません.使用条件変数には、主に2つのアクションがあります.1つのリソースの使用を待つスレッドは、「条件変数が真に設定されている」を待ちます.別のスレッドは、リソースを使用した後に「真の条件を設定」します.これにより、スレッド間の同期が保証されます.このように重要な問題は、条件変数が正しく修正され、条件変数が特殊な保護を受けることを保証することであり、実際の使用では反発ロックがこのような保護者の役割を果たしていることである.Linuxは、条件変数の操作に関する一連の関数を提供します.
    操作条件変数の関数
    手紙の数
    こうりょく
    pthread_cond_init
    初期化条件変数
    pthread_cond_wait
    条件変数に基づいてブロックされ、無条件待機
    pthread_cond_timedwait
    指定したイベントが発生するまでブロックし、タイミング待ち
    pthread_cond_signal
    特定のスレッドのブロックを解除し、複数の待機スレッドが存在する場合にエンキュー順にいずれかをアクティブにします.
    pthread_cond_broadcast
    すべてのスレッドのブロックを解除
    pthread_cond_destroy
    条件変数のクリア
    反発ロックと同様に,条件変数の初期化にも2つの方式があり,1つは時静的付与法であり,マクロ構造定数PTHREAD_COND_INITIALIZERは反発ロックを与え、操作文は以下の通りである.
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    もう1つの方法は、関数pthread_を使用することです.cond_init
    int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);

    ここでcond_attrパラメータは条件変数の属性であり,実装されていないため,彼の値は通常NULLである.
    待機条件の成立には2つの関数があります:pthread_cond_waitとpthread_cond_timdwait.
    int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
    int pthread_cond_timdwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime);

    pthread_cond_wait関数はmutexによって指し示す反発ロックを解放し、同時に現在のスレッドがcondについて指し示す条件変数を条件が信号によって起動されるまでブロックする.通常、条件式は反発ロックの保護の下で評価され、条件式が偽である場合、スレッドは条件変数に基づいてブロックされます.1つのスレッドが条件変数の値を変更すると、条件変数は、条件変数を待つスレッドをブロック状態から終了させる信号を得る.
    pthread_cond_timdwait関数は、上記の用法と同様に、条件変数が信号を得るまでブロックされるか、abstimeによって指定された時間が経過するか、すなわち、所定の時刻まで条件が満たされない場合にブロックされる.ETIMEOUTに戻り、待ちを終了します.
    スレッドが条件変数によってブロックされた後、関数pthread_を介してcond_Signalとpthread_cond_broadcastがアクティブになります.
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond);

    pthread_cond_Signal()は、1つの待機条件が成立するスレッドをアクティブにし、複数の待機スレッドが存在する場合、エンキュー順にそのうちの1つをアクティブにする.pthread_cond_broadcast()は、すべての待機スレッドをアクティブにします.
    条件変数が使用されなくなった場合は、それをクリアする必要があります.
    int pthread_cond_destroy(pthread_cond_t *cond);

    この関数はcondが指す条件変数をクリアします.注意:この条件変数は、スレッドが待機していない場合にのみ消去されます.そうしないと、EBUSYに戻ります.
    次は条件変数の使用です.2つのスレッドが起動され、同じ条件変数が待機します.
    #include 
    #include 
    #include 
    
    pthread_mutex_t mutex;
    pthread_cond_t  cond;
    
    void * thread1(void *arg)
    {
    	pthread_cleanup_push(pthread_mutex_unlock, &mutex);
    
    	while(1){
    		printf("thread1 is running
    "); pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); printf("thread1 applied the condition
    "); pthread_mutex_unlock(&mutex); sleep(4); } pthread_cleanup_pop(0); } void * thread2(void *arg) { while(1){ printf("thread2 is running
    "); pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); printf("thread2 applied the condition
    "); pthread_mutex_unlock(&mutex); sleep(1); } } int main() { pthread_t tid1, tid2; printf("condition variable study!
    "); pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); pthread_create(&tid1, NULL, (void *)thread1,NULL); pthread_create(&tid2, NULL, (void *)thread2,NULL); do{ pthread_cond_signal(&cond); }while(1); sleep(50); pthread_exit(0); }

    実行結果フラグメントは次のとおりです.
    condition variable study!
    thread1 is running
    thread1 applied the condition
    thread2 is running
    thread2 applied the condition
    thread2 is running
    thread2 applied the condition
    thread2 is running
    thread2 applied the condition
    thread2 is running
    thread2 applied the condition