電子書籍の厳選無料作品が豊富!

Microsoft visual C#2005でスレッドを使用したプログラムを行なっているのですが,意図した動作を行えません.

意図する動作は,連番で複数の画像を持つ二つのフォルダ(0,1とします)から画像をリストに読み込み,フォルダ0読み込み→フォルダ0内の画像を順に続けて表示(この間にフォルダ1の読み込み)→3秒置いて→フォルダ1内の画像を順に続けて表示です.

    //再生ボタン
private void button_Click(object sender, EventArgs e)
{
      //フォルダ0読込
directory = directories[0];
Read = new Thread(new ThreadStart(read_image));
Read.Start();

if(now_loading == true)
{
Read.join();
}

//フォルダ0表示
Showing1 = new Thread(new ThreadStart(Show));
Showing1.Start();

//フォルダ1読込
directory = directories[1];
Reads = new Thread(new ThreadStart(read_images));
Reads.Start();
}

      if(now_showing == true)
{
Showing1.Join();
}

if(now_showing == true)
{
Reads.Join();
}

//フォルダ1表示
Showing2 = new Thread(new ThreadStart(Show));
Showing2.Start();

}

    //動画表示
private void Show()
{
Stopwatch sw = new Stopwatch();

//リストに入っているファイルの数をfile_numbersに入れる
file_numbers = img.Count;

//1枚目を表示
pictureBox1.Image = img[0];

//フレーム番号のリセット
now_show = 1;

//時間計測:開始
sw.Start();

while (now_show < file_numbers)
{
if (33 <= sw.ElapsedMilliseconds)//一枚の表示時間が33ミリ秒以上になったら次の画像を表示
{
//前フレームを消去
img[0].Dispose();
img.RemoveAt(0);

//画像を表示
pictureBox1.Image = img[0];

//カウントを進める
now_show++;

//時間計測再スタート
sw.Reset();
sw.Start();
}
}

//時間計測:停止
sw.Stop();

//最終フレームを消去
pictureBox1.Image = null;
img[0].Dispose();
img.RemoveAt(0);

Thread.Sleep(3000);//3秒待機
}

//読込
private void read_images()
{
int read_numbers = Directory.GetFiles(directory).Length;

now_loading = true;

for (int i = 0; i < read_numbers; i++)
{
img.Add(Image.FromFile(String.Format("{0}\\{1:000}.jpg", directory, i)));
}

now_loading = false;
}


//初回読込
private void read_image()
{
now_loading = true;

//フォルダ内のファイル数の取得
file_numbers = Directory.GetFiles(directory).Length;

//画像ファイルを読み込む
img = new List<Image>();
for (int i = 0; i < file_numbers; i++)//ファイル数の数
{
img.Add(Image.FromFile(String.Format("{0}\\{1:000}.jpg", directory, i)));
}

now_loading = false;
}


joinでスレッドの終了を待つということなので,B表示スレッドを開始する前に A表示スレッド.join を置けばよいと考えたのですが,タイマーを配置して見たところA表示のスレッドのjoinを待つ前にB表示のスレッドが始まってしまっているようです.(Show内の始めと終わりの部分でテキスト表示すると,フォルダ1の再生後にフォルダ0のShowスレッドが終了していました)
また,各画像フォルダの再生時間もタイマで図るとほぼ指定した通りなのですが自分の時計で計測すると画像500枚で2秒程度短くなってしまいます.
joinを使っている時点で表示スレッドを関数にした方が良いのかもしれませんが,何故思うように動かないのか知りたいです.
アドバイス頂けると嬉しいです.

A 回答 (2件)

>A表示のスレッドのjoinを待つ前にB表示のスレッドが始まってしまっているようです.



>Read.Start();
>if(now_loading == true)

この間にスレッド切り替えが発生して、read_image()のnow_loading = trueが処理される。
という保証は無いかと思いますが、どうなんでしょうかね?

Thread.Sleep()でスレッド切り替えの機会を入れれば…というのはありますが、それでも確実ではないでしょう。
# 何より、イベントハンドラ内でそういう待ちは入れるべきではない…かと。

now_loadingとかnow_showingとかはスレッドセーフなオブジェクトなんですか?
>if(now_loading == true)
で判定した直後にスレッド切り替えが発生してnow_loadingの値が変わる…とかいうことはないのでしょうか?
# 該当処理中に「now_loading」が変化しないから最適化で実効コードが消える。
# なんてことはC#であるんでしょうかね?
    • good
    • 0
この回答へのお礼

回答ありがとうございます.

【訂正】
フォルダ1の前の
if(now_showing == true)
{
Reads.Join();
}
のnow_showingはnow_loadingの間違いです.失礼しました.


>>Read.Start();
>>if(now_loading == true)

>この間にスレッド切り替えが発生して、read_image()のnow_loading = trueが処理される。
>という保証は無いかと思いますが、どうなんでしょうかね?

ご指摘頂いたとおり,確認してみたところ先にif文の方が判定される場合があり,その時にスレッドが終了していないままjoinもスルーされていたようです.
単純にif(now_loading == true)の下りを削除し,joinは必ず実行されるようにしたところ,出力でチェックする限りは最初のフォルダの再生終了前にBが再生され始めるということは無くなりました.
また,再生時間も実測時間と同じになりました.

ただ,Reads.Join();の部分でInbaridOperationExeceptionはハンドルされませんでしたとエラーが出たのですが,対象とするスレッドが終了しているのが理由と見てもいいのでしょうか.joinは対象とするスレッドが終了している場合,特に何もエラーは出ないと考えていたのですが.
この部分のみ,if(now_loading==true)で囲んでみたりもしたのですが,読込スレッド内のnow_loadingのtrue(もしくはfalse)切り替えとかぶってしまうのか,同様のエラーが出ました.
また,読み込みが終わってから表示を始めているのですが,一つ目のフォルダ再生の際にどうしてもカクカクとした動きになってしまうのですが,メモリを見る限り不足している様子はないのですが,解決策はあるのでしょうか….

joinが何故スルーされるか分からなかったので,原因が分かって良かったです.有難うございます.

now_loadingをスレッドセーフにすれば良いのかとdalegate型を使おうと一から調べているのですが,理解が遅くもう少し掛かりそうです.
お気づきのところがあれば,引き続きご指摘頂けると嬉しいです.

お礼日時:2012/08/10 21:31

C#自体たいして経験があるワケではないのですが…


# しかも使っているのは.NET Framework 4の方。
# 自宅PCにインストールしてあるVS2005StdのC#は使っていないし…。

>ただ,Reads.Join();の部分でInbaridOperationExeceptionはハンドルされませんでしたとエラーが出たのですが,対象とするスレッドが終了しているのが理由と見てもいいのでしょうか.

たぶん、別…ではないですかね。
スレッド内の処理のどれかが例外出しているんじゃないかと思われますが……。

>joinは対象とするスレッドが終了している場合,特に何もエラーは出ないと考えていたのですが.

ヘルプだとそう書かれてますね。
タイミングなんですかねぇ……。

ところでInbaridOperationExeceptionの例外にはどんなメッセージついていましたか?
    • good
    • 0
この回答へのお礼

>>ところでInbaridOperationExeceptionの例外にはどんなメッセージついていましたか?
試しに,if(now_loading==true)の下りを全て削除し,呼び出すスレッドの名前を全て統一してみたところ,InvaridOperationExeceptionは出なくなりました.以降そのまま進めているのでメッセージは確認できませんでした.ただ,10回程度回しただけなので運が良かっただけかもしれません….
Joinメソッドは対象とするスレッドが終了している場合は問題ないようですが,ThreadState.Unstarted 状態のスレッドで Join を呼び出すことはできないようなのでそれが原因かもと思っています.

表示が想定どおりに行かないので,Thread.Abortを取り入れたところ一つ目のフォルダは問題なく表示されるようになったのですが,やはり二つ目が想定表示時間よりも短くなるようです.
Joinの挙動についてはおかげで解決しましたので,Abortは新たに質問し直させて頂きました.一旦この質問版は閉じさせて頂きます.

回答有難うございました.

お礼日時:2012/08/13 21:30

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!