SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

pthreadについて

pthreadについて(条件変数・モデル)

pthreadにおける条件変数の扱い方、気をつける点、簡単なアプリケーション


4. 条件変数(3/3)

4.2.3 スレッド生成と条件変数の同期

 しかし、更に前ページのプログラムには危険な箇所が隠れてます。呼び出し元(main関数内)のsleep関数をコメントアウトすると、環境によってはプログラムはサスペンドします。

 上記サンプルは、呼び出し元がpthread_cond_broadcastを行う事で生成されたスレッドが一斉に起きて処理(この場合はfprintfを実行)します。しかし一番最後に生成されたスレッドは、まだpthread_cond_waitにたどり着いていない時にpthread_cond_broadcastが届く可能性があります。

 こうなると最後に生成されたスレッドはpthread_cond_waitで待ち続け、呼び出し元はpthread_joinで待ち続けるという、いわゆるデッドロック状態に陥ります。

 この改訂版は下記の通りです。

cond_test_4th.c
/* gcc cond_test_4th.c -o cond_test_4th -W -Wall -lpthread */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define THREAD_MAX 256

void * temp_func( void * arg );

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  ready_cond  = PTHREAD_COND_INITIALIZER;
int   start_flg = 0;

int main( int argc, char ** argv ) {

    pthread_t   pt[THREAD_MAX];
    int         all_thr_count = 0;
    int         i;

    if( argc < 2 ) return( 1 );
    all_thr_count  = atol( argv[1] );
    if( all_thr_count <= 0 || all_thr_count > THREAD_MAX ) {
        return( 1 );
    }

    pthread_mutex_lock( &ready_mutex );
    for( i = 0; i < all_thr_count; i ++ ) {
        pthread_create( &pt[i], 0, temp_func, &i );
        pthread_cond_wait( &ready_cond, &ready_mutex );
    }
    pthread_mutex_unlock( &ready_mutex );

    start_flg = 1;
    pthread_cond_broadcast( &cond );
    for( i = 0; i < all_thr_count; i ++ ) {
        pthread_join( pt[i], 0 );
    }
    return 0;
}

void * temp_func( void * arg ) {

    int  index = *( int * )arg;
    pthread_cond_signal( &ready_cond );

    fprintf( stdout, "start thread index:[%d] input!!\n", index );

    pthread_mutex_lock( &mutex );
    while( start_flg == 0 ) {
        pthread_cond_wait( &cond, &mutex );
    }
    pthread_mutex_unlock( &mutex ) ;

    fprintf( stdout, "end thread index:[%d] output!!\n", index );
    return 0;
}

 グローバルな変数を使ってしまうのは忍びないですが、スレッド内で条件変数を参照する際、更に条件を設定します(start_flg == 0)。呼び出し元からはpthread_cond_broadcastを発行する前に条件式を外しておきます。こうする事で最後のスレッドはpthread_cond_waitを行うことなく処理を続行できます。

 もう1つの危険箇所がスレッド内のpthread_cond_signalと呼び出し元のpthread_cond_waitです。

 スレッド内で引数を受け取ったら呼び出し元向けにpthread_cond_signalを発行していますが、果たして呼び出し元がpthread_cond_waitで待ち状態にいるのか、不明です。呼び出し元がpthread_cond_waitに居ない時にスレッドがpthread_cond_signalを発行すると、呼び出し元はsignalを取りそこない、その結果pthread_cond_waitでデッドロックが発生します。

 上記プログラムを何度か動かすと、環境によっては、スレッドが指定した数まで起動しないでサスペンドする場合が発生します。私の環境では平均で5回に1回程再現します。

 引数を受け取る際には呼び出し元がpthread_cond_waitで待ち状態に入っている事を保障された状態で処理し、pthread_cond_signalを発行するようにします。

cond_test_5th.c
/* gcc cond_test_5th.c -o cond_test_5th -W -Wall -lpthread */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define THREAD_MAX 256

void * temp_func( void * arg );

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  ready_cond  = PTHREAD_COND_INITIALIZER;
int   start_flg = 0;

int main( int argc, char ** argv ) {

    pthread_t   pt[THREAD_MAX];
    int         all_thr_count = 0;
    int         i;

    if( argc < 2 ) return( 1 );
    all_thr_count  = atol( argv[1] );
    if( all_thr_count <= 0 || all_thr_count > THREAD_MAX ) {
        return( 1 );
    }

    pthread_mutex_lock( &ready_mutex );
    for( i = 0; i < all_thr_count; i ++ ) {
        pthread_create( &pt[i], 0, temp_func, &i );
        pthread_cond_wait( &ready_cond, &ready_mutex );
    }
    pthread_mutex_unlock( &ready_mutex );

    start_flg = 1;
    pthread_cond_broadcast( &cond );
    for( i = 0; i < all_thr_count; i ++ ) {
        pthread_join( pt[i], 0 );
    }
    return 0;
}

void * temp_func( void * arg ) {

    pthread_mutex_lock( &ready_mutex );
    int  index = *( int * )arg;
    pthread_cond_signal( &ready_cond );
    pthread_mutex_unlock( &ready_mutex );

    fprintf( stdout, "start thread index:[%d] input!!\n", index );

    pthread_mutex_lock( &mutex );
    while( start_flg == 0 ) {
        pthread_cond_wait( &cond, &mutex );
    }
    pthread_mutex_unlock( &mutex ) ;

    fprintf( stdout, "end thread index:[%d] output!!\n", index );
    return 0;
}

 上記から、スレッドはただ生成するだけでも決して油断ならない事が分かったと思います。再現性の薄いバグが混在しやすく、回避するにはプログラムも泥臭くなりやすいです。常に呼び出し元と呼び出し先の状態を気に留めながら、条件変数を使用し慎重なコーディングを心がけるようにしましょう。

次のページ
5. モデル

この記事は参考になりましたか?

pthreadについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

(株)DTSに勤てます。WebアプリやJavaやLL等の上位アプリ環境を密かに憧れつつも、ず~っとLinuxとかHP-UXばかり、ここ数年はカーネル以上アプリ未満のあたりを行ったり来たりしています。mixiもやってまして、こちらは子育てとか日々の日記メインです。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1894 2008/08/20 15:59

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー