4.2 プロセス間の同期問題
Menu Menu
課題3 生産者消費者問題
mp-sem-prodcons.c に、セマフォを使った生産者消費者問題の解の一部を示す。このプログラムの生産者プロセスの部分は完成されている。課題は、消費者プロセスの部分を完成させることである。以下に半完成のプログラム mp-sem-prodcons.c を示す。生産者消費者問題は計数セマフォを使うことにより実現できるが、pthread には残念ながら計数セマフォはない。その代わり条件付き変数があるので、それを用いる。
/* mp-sem-prodcons.c -- セマフォを使った生産者消費者問題の解 $Header$ Start: 1995/03/01 19:54:55 */ #include <stdio.h> #include <pthread.h> void *consumer(void *); void *producer(void *); pthread_t thread_consumer,thread_producer; #define BUFFSIZE 3 /* バッファの大きさ, 教科書では N */ int i_p ; /* 書き込む位置, 教科書では i */ int j_c ; /* 読み出す位置, 教科書では j */ /* POSIX thread にはセマフォがないので、自分で作ります。 */条件付き変数と、セマフォを組み合わせて計数セマフォを作る。cond は、待ち合わせを行う軽量プロセスのキューがである。
typedef struct sem_t { int value; pthread_mutex_t mutex; /* セマフォの値の整合性を得るためのロック */ pthread_cond_t cond; /* 待ち合わせのキューを作るための条件付き変数 */ } sem_t,* sem_ptr; void sem_init(sem_t *,int); void sem_v(sem_t *); void sem_p(sem_t *);計数セマフォの初期化
void sem_init(sem_t *sem,int value) { pthread_mutex_init(&sem->mutex,NULL); pthread_cond_init(&sem->cond,NULL); sem->value = value; }P動作、バイナリセマフォと違って、複数の待ち合わせが行われることがある。
void sem_p(sem_t *sem) { /* まずvalueをみるためだけにlockする */ pthread_mutex_lock(&sem->mutex); while(sem->value == 0) { /* もう、残ってない */ /* 条件付き変数に自分を登録して、ロックを解放して、他の プロセスが資源を解放してくれるのを待つ */ pthread_cond_wait(&sem->cond,&sem->mutex); } /* 自分の分の資源があったので、それを確保する */ sem->value--; pthread_mutex_unlock(&sem->mutex); /* ロックを解放する */ }V動作では、解放される資源は一つしかないので、一人だけしか起こす必要はない。
void sem_v(sem_t *sem) { /* まずvalueをみるためだけにlockする */ pthread_mutex_lock(&sem->mutex); /* 資源を解放する */ sem->value++; pthread_mutex_unlock(&sem->mutex); /* 他に資源が待って寝ている人がいれば、その人を起こす */ pthread_cond_signal(&sem->cond); } sem_t mutex_c ; /* 生産者間の相互排除, 教科書では a */ sem_t mutex_p ; /* 消費者間の相互排除, 教科書では b */ sem_t rem ; /* バッファの空き容量, 教科書では e */ sem_t count ; /* バッファ内のデータ数, 教科書では f */ int buffer[BUFFSIZE] ; /* バッファ */ int nloop = 10 ; /* 何回繰り返して止るか */ int main(int ac,char *av[]) { /* mp_user_main() は、軽量プロセスを2個作り終了する。*/ j_c = 0 ; i_p = 0 ; sem_init( &mutex_p,1 ); /* 相互排除用のセマフォの初期化 */ sem_init( &mutex_c,1 ); /* 相互排除用のセマフォの初期化 */ sem_init( &rem,BUFFSIZE ); /* バッファの空きは、N, e=0 */ sem_init( &count,0 ); /* バッファは空, f=0 */ pthread_create(&thread_consumer, NULL, &consumer, NULL); pthread_create(&thread_producer, NULL, &producer, NULL); pthread_join(thread_consumer, NULL); pthread_join(thread_producer, NULL); } void * producer(void *arg) /* 生産者プロセス */ { int x ; int i ; x = 0 ; fprintf(stderr,"producer(): started.\n"); for( i=0 ; i<nloop ; i++ ) { sem_p( &rem ); /* 空きができるのを待つ */ sem_p( &mutex_p ); /* 生産者間の相互排除 */ x = i * 2 ; /* 次の情報を生産する。*/ fprintf(stderr,"producer(): put %d.\n", x ); buffer[i_p] = x ; /* バッファに情報を入れる */ i_p++ ; /* i_p = (i_p+1) % BUFFSIZE */ if( i_p>=BUFFSIZE ) i_p = 0 ; sem_v( &mutex_p ); /* 生産者間の相互排除 */ sem_v( &count ); /* 情報を入れたことを伝える */ } } void * consumer(void *arg) /* 消費者プロセス */ { /* 課題は、この部分を完成させることである。最終的に生産者と消費者は、対称 的なプログラムになるはずである。 */ }mp-sem-prodcons.cのコンパイル方法、実行方法を以下に示す。(このプログラムは未完成であり、そのままでは動作しない)
% make mp-sem-prodcons % ./mp-sem-prodconsプログラムのテスト中に、プログラムの動作が進まなくなることもありえる。これは、デッドロック(dead-lock) と呼ばれる。また、複数のthreadが動作しているのだが、全体として動作が進まなくなる場合もある。これは、ライブロック(live-lock)といわれる。
課題4 複数の生産者と複数の消費者
課題3では、生産者と消費者プロセスがそれぞれ1つづつしか生成されない。これを変更して、ふくすうの生産者プロセスと複数の消費者プロセスを生成し、動作させなさい。生産者プロセスと消費者プロセスの数は異なる場合が普通である。しかし、生産量と消費量がバランスしないとデッドロックに陥る可能性がある。
報告書
それぞれの実験に付いて、作成したプログラム、その説明 (Makefile についても説明すること)、および、その実行結果を付けなさい。この実験では、プログラムの説明では、フローチャートを付加する必要はない。開発環境と実行環境 (計算機、オペレーティング・システムのバージョン、コンパイラ)を載せなさい。それぞれの実験について、プログラム作成に要した時間を書きなさい。
一般的な注意
報告書は、日本語または英語で記述すること。プログラム、表、図、数式の羅列は、報告書とは認めない。図は必ず本文から参照すること。数学における証明のように、示すべき結論、用いる仮定と前提、推論の詳細について論理的に記述しなさい。