巡回バッファを使用した生産者-消費者問題について

このQ&Aのポイント
  • ダイテル社のJavaプログラミングvol.2という本のサンプルコードで、巡回バッファを使用した生産者-消費者問題を再現しています。
  • このプログラムでは、生産者スレッドが0~9の整数を書き込み、消費者スレッドがそれを読み込むことで生産者-消費者の関係を再現しています。
  • ただし、プログラムの実行結果について質問があります。生産者スレッドが値を書き込み終わった後、消費者スレッドがwhileループを抜ける前にスレッドが終了してしまう可能性があると思われるため、この挙動について説明しました。
回答を見る
  • ベストアンサー

巡回バッファを使用した生産者-消費者問題について

ダイテル社のJavaプログラミングvol.2という本のサンプルコードなのですが、このプログラムは、5つの要素を持つ配列を共有バッファとし、それを生産者スレッドと消費者スレッドの共有資源とするものです。 ProduceIntegerクラスが生産者、ConsumeIntegerが消費者に対応しています。共有バッファは、HoldIntegerクラスのインスタンス変数holdvalue[]です。生産者は共有バッファに0~9の整数を書き込み、消費者は書き込まれた整数を読み込むことによって生産者-消費者を再現しています。実行結果について、正しく生産者が生産した0~9を漏れることなく消費しているのですが、ここで質問です。 生産者スレッドは、0~9の値を書き込むと、HoldIntegerクラスのmoreDataをfalseに設定して死亡します。 消費者スレッドは書き込まれた値を全て消費するまで58行目のwhile文で繰り返されます。生産者スレッドのほうが、消費者よりもsleep時間が短いので、大抵生産者スレッドは先に値を書き込み終わります。生産者スレッドが値を書き込み終わりmoreDataフラグをfalseにしたあたりのところ(実行結果26行目)で疑問があるのですが、まず生産者スレッドのwhile条件は(moreDataがfalse かつ writeLocとreadLocが同じ)でない時です。もし、生産者スレッドがmoreDataフラグをfalseにした次の瞬間に消費者スレッドのwhile条件が評価された場合は、実行結果を見て分かるようにreadLoc=0, writeLoc=0なのでそこでwhileを抜けて消費しおわってない状態でスレッドが死亡してしまうと思ったのですがどうでしょう?sleepTimeを乱数でとっているためタイミングによって左右されるのではないでしょうか。納得ができないので、どうか説明よろしくお願いします。説明分かりにくかったらすみません。 ソース:http://izumo.cool.ne.jp/precious7/SharedCell.java 実行結果:http://izumo.cool.ne.jp/precious7/result.txt

  • Java
  • 回答数1
  • ありがとう数1

質問者が選んだベストアンサー

  • ベストアンサー
  • PecoPlus
  • ベストアンサー率76% (144/188)
回答No.1

 おはようございます。  確かに、おかしいですね。  ほとんどの場合、5,6,7,8,9が消費されないまま、終了してしまいますね。  HoldIntegerクラスのgetmoreDataメソッドは、  readLoc == writeLoc の場合、バッファが満タンの場合と、空の場合の二通りあるのだから、  正しくは public boolean getmoreData(){   if ( moreData == false && readable == false)     return false;   return true; }  こうでしょうねぇ。  それにしても、妙に読みにくいサンプルですよねぇ。  バッファにたまっている要素数を、カウントしていったほうが、読みやすくなると思うんですけどなぁ。  そうすれど、こんな簡単な失敗もしなくてすむのに・・・。  あんまり、よくない本なんですかね。

poplun
質問者

お礼

素早い回答ありがとうございます! 確かにreadable==falseという条件なら納得できます。 本が間違ってることはないって頭で考えてたんで気づかなかったです。 この本にのってるサンプルは、あまり良いものではないかもしれませんね・・・ ありがとうございました!

関連するQ&A

  • 問題がとけません

    以下の問題がわかりません。 教えていただけますでしょうか? 以下の仕様を満たすプログラムを作成せよ。 キーボードから2整数を読み込み,読み込んだ順に標準出力にそれらの値の10進数表現をスペース文字で区切って1行に書き出し,改行する。読み込んだ一番目の整数の値が1000でなければ,次の2整数を読み込み同様に書き出す。一番目の整数の値が1000のときプログラムの実行を終了する。 ヒント------------------------------------------------- stdio.hをインクルードする。 読み込んだ値を保持するための2つの整変数を宣言して定義する。 2つの整数を読み込んで,その値を書き出すことを繰り返す部分は,次のdo-whileループで実現する。 do { (1) //2整数の読み込み //2整数の書き出し } while(条件); (2) プログラムの実行は上から順に行われるが,(2)で条件が真であれば(1)に戻り,そこから再び下方に順番に実行される。条件が真でなければ,(2)の次の行から下に順番に実行される。したがって,(2)の条件が満たされているかぎり,(1)と(2)の間を繰り返し実行することになる。このdo-whileループは,必ず1回は実行される点で,whileループと異なる。 行中に//があると,そこから行末まではコメントとみなされ書かれている内容は無視される。また,任意の位置に書かれた,一対の/*と*/の間もコメントとみなされる。 この課題の繰り返しの条件は,読み込んだ一番目の整数が1000でないこと。 iを整変数として, i != 1 という論理式の値は,iが1のとき偽,iが1でないとき真である。 2種類ある等値演算子の1つであるこの"!="という2項演算子(演算子の両側に1項ずつの2項を使う演算子)による演算結果は,両側の2つのオペランド(演算子が働きかけるもの)の値が「等しくない」とき真,それ以外のとき偽である。もう一つの等値演算子は"=="で,演算結果は2項が「等しい」とき真,それ以外のとき偽となる。 

  • 「複数の」生産者消費者問題 in Java

    基本的に生産者消費者問題なんですが Aが物を作るとBに渡し、Bが物を作るとCに渡し、Cが物を作るとDに渡す、というように 複数の生産者/消費者のベルトコンベヤーでの流れ作業を想定しています。 で以下のように組んでみたんですが、 B自身が「消費した/してない」をB自身に知らせていないので Bは勝手に作り始めてしまいます。 public class Main { public static void main(String args[]) { ConveyerBelt stationAB = new ConveyerBelt(); ConveyerBelt stationBC = new ConveyerBelt(); ConveyerBelt stationCD = new ConveyerBelt(); Worker producerA = new Worker(stationAB); Widget consumerB = new Widget(stationAB); Worker producerB = new Worker(stationBC); Widget consumerC = new Widget(stationBC); Worker producerC = new Worker(stationCD); Widget consumerD = new Widget(stationCD); producerA.start(); consumerB.start(); producerB.start(); consumerC.start(); producerC.start(); consumerD.start(); } } ConveyerBelt()バッファーがABとBCとCDの三つというのは正しいと思うんですけど スレッドはAとBとCとDの四つだけ作ればいいんでしょうか? でもその場合はConveyerBelt class自体はどんな風にいじればいいのでしょうか? ちなみにConveyerBelt()バッファーを一つにすればみんな同期しますが 当然ながら流れ作業になりません。 分からないところがあれば補足しますので、どなたかご教授下さい。 どうかお願いします。m(__)m

  • クラスを使用した問題

    問題 Mainクラス 1.実行時に引数を付けて実行する。 2.判定クラスのオブジェクトを生成する。 3.args(実行時の引数)から値を取得する。 4.argsから取得した値を引数として、判定クラスのnameメソッドを実行する。 5.argsから取得した値を引数として、判定クラスのageメソッドを実行する。 6.argsから取得した値を引数として、判定クラスのbirthメソッドを実行する。 判定クラス nameメソッド(引数=String) 1.String型の引数を一つ受け取る 2.受け取った引数の値が「name」だった場合、名前を表示する。 戻り値なし。 ageメソッド(引数=String) 1.String型の引数を一つ受け取る 2.受け取った引数の値が「age」だった場合、名前を表示する。 戻り値なし。 birthdayメソッド(引数=String) 1.String型の引数を一つ受け取る 2.受け取った引数の値が「birthday」だった場合、名前を表示する。 戻り値なし。 実行結果 java NameAgeBirth name 岩田 java NameAgeBirth age 27歳 java NameAgeBirth birthday 5月16日 参考例が欲しいです。良かったら参考例をください。よろしくお願いします。

  • グローバル変数が正常に参照できない?

    Windows2000、VC++6.0 でMFCを使ってダイアログベースのEXEを作っています。 以下のようにグローバル変数をループ処理の終了条件に使ってみたのですが、Release版でビルドしたEXEだと意図した動作となりませんでした。 何か原因について心当たりのある方がいればご教授願います。 ------------------------------------------- BOOL bThreadEndFlag; // 外部変数 ●FuncA()関数でスレッドを作成します。 FuncA() {  bThreadEndFlag = FALSE;  CreateThread(..., ThreadFunc, ...);  Sleep(60000);  bThreadEndFlag = TRUE;  ◆     ・     ・ } ●スレッドではフラグがFALSEの間ループします。 ThreadFunc() {  while(bThreadEndFlag == FALSE ) {   X = Y;   ★  } } ------------------------------------------- 上記ではスレッド作成後、60秒後にはフラグがTRUEになり、スレッドが終了することを想定した造りになってます。 しかし、実際にはループ処理が終わらず、スレッドが終了しませんでした。 論理的にはちょっと理解できません。。。 いろいろ試して、以下の場合は正常に動作しました。 (1)コードは全く同じままで、Debug版でビルドした場合はOKでした。 (2)★の部分に「Sleep(0)」を追加した場合はRelease版でもOKでした。 <備考> ◆の位置でフラグの値がTRUEになっていることは確認しました。

  • マルチスレッド内のループについて

    こんにちは。 開発環境は VC++6.0 SDI マルチスレッドがあり、2つの処理を行う関数が書かれています。 このスレッドはダイアログボックスに配置したストップボタンを押すとフラグFALSEになりループを終了させます。 2つの関数は、int型の整数を引数にして、処理を行います。 整数はある値に達すると 0 になり永遠にループを続け、2つの処理を行います。 と言う意味合いでプログラムを書きました。(書いたつもりです) (1)この書き方ですと、for内のループが動いている時に、右上の×ボタンでダイアログを閉じると [Debug Assertion Failed!]と言う警告文が出て強制的に終了してしまいます。 ストップボタンを押してもcount=10になるまではループしています。(当然ですが・・。) そもそもマルチスレッドの中にこのような形でfor文を入れるのは間違っているのでしょうか? どのような書き方にすれば良いのでしょうか? よろしくお願い致します。 bool m_flags;//スレッド内の処理を続けるか示すフラグ UINT CabcDlg::thread(LPVOID pParam)// { CabcDlg *pInst = (CabcDlg *) pParam; while(pInst->m_flags){//ストップボタンが押されると終了する。 for (int count = 0; count<10; count++ ) { pInst->OnSend(count); //処理A pInst->OnReceive(count);//処理B } } return 0; }

  • java のスレッドについて

    java のスレッドについて javaでスレッドを作って処理をさせたいと思います。 メインのクラスでスレッドAを呼び出し、スレッドAで計算をしてその結果をメインのクラスで利用したい場合、一番簡単な方法は何でしょうか? なお、メインのクラスは、 while (Thread.isAlive()); な感じでスレッドが終わるまで処理を止めておきたいと思います。 簡単なコードを書いていただけないでしょうか?

    • ベストアンサー
    • Java
  • C++のこの書き方の意味教えて

    C++初心者です。 他人が作ったプログラムを修正しないといけなくなったのですが、調べても意味がわからない箇所が 多々あります。 どなたか、得意な方、だいたいでよいのでわかる方、助けてください。 1.    static Thread *SecondProcess =         new Thread(Form1 , false) ちなみにthread はクラス名で、Thread(Form1 , false)はそのクラス内のメンバー関数のようです。 この文を実行したあとに、 __fastcall Thread::Thread(TForm1 *InForm , bool   CreateSuspended) : TThread(CreateSuspended) { Form1 = InForm; } という違うユニットの文を実行しているようなのですが どうして次にここにいくのかもよくわかりません。 Thread(TForm1 *InForm , bool CreateSuspended) : TThread(CreateSuspended) の:はどういう意味なのでしょう?

  • スレッドの停止方法の確認

    スレッドの停止方法の質問。 停止可能なスレッドを、私は以下のような形で書く事が多いのですが、インターネットで同様な コードを検索すると、2行目の停止フラグの宣言に volatile が無いものが無数に見つかります。 JSR-133 の簡単な解説を読んで、私なりに解釈した結果 volatile は必要と判断したのですが、これは間違いなのでしょうか? volatile は多少重たいので、できれば外したいのですが、 外してよい根拠が見つかりません。 真偽をご存知の方、よろしくお願い致します。 class MyThread extends Thread { private volatile boolean done = false; public void terminate(){ done = true; interrupt(); } public void run(){ while(!done){ //処理 } } }

    • ベストアンサー
    • Java
  • 条件変数を用いた有限バッファ問題を考えています。

    皆さんこんにちは。 当方、プログラミングを勉強中の学生です。 条件変数を用いた有限バッファ問題を考えております。 以下に示すソースにおいて、関数produce()は1から1000までの整数を順に生成し、関数consume()はバッファから取り出した値の合計(1から1000までの和、500500となる)を求めるようプログラミングしているつもりなのですが、コンパイルして実行すると思ったような結果となりません。 どこが間違っているかご教授いただければ幸いです。 よろしくお願い致します。 以下、ソースとなります。 #include <stdio.h> #include <pthread.h> #define N 5 int buffer[N]; int inptr = 0, outptr = 0; int count = 0; int i = 0; int j = 0; int sum = 0; pthread_cond_t full = PTHREAD_COND_INITIALIZER; pthread_cond_t empty = PTHREAD_COND_INITIALIZER; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; int produce (void) { i += 1; return i; } void consume (int x) { j += 1; sum += x; } void *producer(void *arg) { int data; for (;;) { if (i >= 1000) break; data = produce(); pthread_mutex_lock(&lock); while (count > N) pthread_cond_wait(&full, &lock); count = count + 1; buffer[inptr] = data; inptr = (inptr + 1) % N; pthread_mutex_unlock(&lock); pthread_cond_signal(&empty); } } void *consumer(void *arg) { int data; for (;;) { if (j >= 1000) break; pthread_mutex_lock(&lock); while (count == 0) pthread_cond_wait(&empty, &lock); count = count - 1; data = buffer[outptr]; outptr = (outptr + 1) % N; pthread_mutex_unlock(&lock); pthread_cond_signal(&full); consume(data); } } int main() { pthread_t a, b; pthread_create(&a, NULL, producer, NULL); pthread_create(&b, NULL, consumer, NULL); pthread_join(a, NULL); pthread_join(b, NULL); printf("sum = %d\n", sum); return 0; }

  • おしえてください1

    整数を入力し,その値とアドレスを表示させよ <実行結果> 整数を入力せよ:15 値 アドレス 15 22CCC4

専門家に質問してみよう