R.Tanaka.Ichiro's Blog

主にC# な話題です

目次

Blog 利用状況

ニュース

簡単な実行中ウィンドウ(その3)

簡単な実行中ウィンドウ(その2)
http://blogs.wankuma.com/rti/archive/2007/02/07/61401.aspx

上記(昨日)の話です。


裏方作業

というオブジェクト名は、かなり気に入っています。


などと書いたところ、えムナウさんに「FormにBackgroundWorkerコンポーネントを貼り付けないなんて気に入らない。 」という見事な返しをいただきまして(w)、フォームオブジェクト1本にまとめることにしました。

デザイナが partial なクラスを作ってくれるため、その部分を記述する必要は当然の如くありません。

private void 簡単な実行中ウィンドウ3_Load(object sender, EventArgs e) {
private void 裏方作業_DoWork(object sender, DoWorkEventArgs e) {
private void 裏方作業_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
private void 裏方作業_ProgressChanged(object sender, ProgressChangedEventArgs e) {

の各イベントは、忘れずに登録して下さい。
それと、WorkerReportsProgress プロパティも true にして下さい。

昨日の、Running フォームのプロパティもデザイナで指定するればコードの記述は不要なので、これも除外します。

BackgroundWorker のオブジェクト名は、昨日と同じ「裏方作業」です。


using System.Windows.Forms;
using System.ComponentModel;

public partial class 簡単な実行中ウィンドウ3 : Form
{
  public 簡単な実行中ウィンドウ3() {
    this.InitializeComponent();
  }

  public void 重たい処理を開始する() {
    this.ShowDialog();  // ここが昨日と違うところですよ。
  }

  private void 重たい処理本体() {
    for (int i = 0; i < int.MaxValue; ++i) {
      // 裏方作業具合を報告(裏方作業_ProgressChanged を呼び出す)
      裏方作業.ReportProgress(i);
    }
    // この処理が終わったら、裏方作業_RunWorkerCompletedが
    // 呼び出される。
  }  

  private void 簡単な実行中ウィンドウ3_Load(object sender, EventArgs e) {
    // 裏方作業開始(裏方作業_DoWork を呼び出す)
    this.裏方作業.RunWorkerAsync();
  }

  private void 裏方作業_DoWork(object sender, DoWorkEventArgs e) {
    // 裏方作業で重たい処理を実行する。
    this.重たい処理本体();  
  }

  private void 裏方作業_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
    this.Close();  // 裏方作業が終わったので実行中を閉じる。
  }

  private void 裏方作業_ProgressChanged(object sender, ProgressChangedEventArgs e) {
    // 進捗度を取得します。
    // プログレスバーやラベルに設定して下さい。
    int i  = e.ProgressPercentage;
  }
}


かなりすっきりしましたね。
重たい処理を実行中のフォームと一本化して、デザイナを上手に使えば、たったこれだけのコードを書くことで実行中画面を表示するスレッドと、実際に重たい処理を実行するスレッドを同時に実行できる訳です。

マルチスレッドが、こんなに簡単に実装できるようになったなんて素晴らしい。

この手法は今後よく使うことになりそうです。

* 上記コードは、コメントをいただいた後に修正しました。

投稿日時 : 2007年2月8日 14:10

Feedback

# re: 簡単な実行中ウィンドウ(その3) 2007/02/08 17:06 R・田中一郎

あれ?
誰からもツッコミが入らないぞ。
赤字の部分とか、皆さんどうしているんでしょうか?

# re: 簡単な実行中ウィンドウ(その3) 2007/02/08 23:22 ネットクラゲ

試していませんが、こんな感じでどうでしょうか?

フィールドにManualResetEventを持って、

裏方作業_RunWorkerCompletedの赤字の部分の代わりに
ManualResetEvent.WaitOne()で待機。

FormのVisibleChangedイベントで
ManualResetEvent.Set()で、終了を通知。

WaitOne()とSet()の呼び出しタイミングが逆転しても
問題なかったはず・・・

# re: 簡単な実行中ウィンドウ(その3) 2007/02/09 9:04 R・田中一郎

>フィールドにManualResetEventを持って、

調べてみました。
なるほど、こんな便利なものがあるんですね。

>WaitOne()とSet()の呼び出しタイミングが逆転しても

これは必須ですね。

# re: 簡単な実行中ウィンドウ(その3) 2007/02/09 16:20 とっちゃん

イベントなんかいりません。
というより、おいらがレビューしたら、 while (! this.Visible) {} がある時点で、他がどんなによくてもNGです。
だって、これがあったら何のためにスレッド化したのかわからないもの。

で、解決方法。

それはスレッドを開始させるタイミングを変えることです。

いまは、スレッドを起動してからダイアログを作ってますよね?
だから、先にスレッドが終了しちゃっても問題が起こらないように終了時にダイアログができるのを待っている。

これじゃぁなんのために非同期にしてるのか?となってしまうw

なので、終了時にまたないようにするためにはどうすればいいか?という視点でもう一度見直してみてください。

ギブアップしたら、答え書きますw

# re: 簡単な実行中ウィンドウ(その3) 2007/02/09 16:51 R・田中一郎

ええっと、Shown イベントから、RunWorkerAsync() するとか・・・(自信無いです)

# re: 簡単な実行中ウィンドウ(その3) 2007/02/09 18:08 とっちゃん

♪よーくかんがえよー。イベント大事だよ~♪

間違ってはいません。ただそれ「だけ」ではありませんがねw

ShowDialogの「中の人」がせっせと送ってくるお手紙は何か?ということとそのお手紙には何が書かれているのか?ということ。
そして、いつお手紙を出してるのか?(いつ来るのかじゃないですよ。間違えないようにw)
の3つです。
これがわかってればどのお手紙が来た時に、何をすればいいか?がわかるようになります。
そしてこれがイベントドリブン型(たいていのGUIなシステムはほとんどがイベントドリブン型です。MACしかり、UNIXしかり、もちろんDOSもです)構造の仕組みでもっとも重要な部分になります。
でもって、最もわかりづらい部分でもあるわけですがwww
イベントに始まり、イベントに終わる。それがイベントドリブンプログラミングの極意ですwww<おい!

ちなみに卵に置き換えると料理になりますw

# re: 簡単な実行中ウィンドウ(その3) 2007/02/09 18:55 R・田中一郎

>間違ってはいません。ただそれ「だけ」ではありませんがねw

ってことは、Shown イベントで、RunWorkerAsync() するのは正解なんですね。

後は何だろう?

>そして、いつお手紙を出してるのか?(いつ来るのかじゃないですよ。間違えないようにw)

うーん、FormClosing イベントで、CancelAsync() するとか。
違います?^^;

# re: 簡単な実行中ウィンドウ(その3) 2007/02/09 22:39 とっちゃん

>後は何だろう?
ほかにも、ShowDialog だからこそな、超有名なイベントがw
よくコンストラクタと混同されてる質問を見かけますよw<最近減りつつあるけどww

実際のところどのイベントを使うか?についてはそのオブジェクト(この場合待機オブジェクトとしてのウィンドウ)をどう使うのか?もあるので、Shown だけ知ってればとか、あえて書かなかった某イベントだけ知ってれば十分ということでもありません。
要はどれだけカードを抱えているか?ってことですからww


>うーん、FormClosing イベントで、CancelAsync() するとか。
ウォ!こっちは、サンプルの域を超えてますよw

でも、FormClosing に着目したというのは非常に重要なことです。というより、このその3の実装であれば、このイベントを処理しないというと「頑健さ」に欠けるということになって、やっぱり及第点は出せないということになるんですけどねww

なぜか、についてはまずはじっくりと考えてみてくださいw
ちょうど連休だしwww<おい!
#わからなかったら、おいらに直メでもOKよww

ということで、こちらについてはひとまずは宿題としておきます。





しかし、このコメント書くのに改めてヘルプ眺めたけど、本当に .NET は楽だねぇw

Nativeだとあれもこれもで考えておかないといけないところがあうけど、そんなののほとんどが意識しなくてもいい、あるいは用意されている、だものw

やっぱり仕事(インストーラじゃない方w)でも使いたいねぇ。。。そんな日は来るんだろうか?ww

# re: 簡単な実行中ウィンドウ(その3) 2007/02/10 14:12 R・田中一郎

>ほかにも、ShowDialog だからこそな、超有名なイベントがw

おおっ、何だろう。
一応、一通り調べてみたつもりだったんですけど、まだ抜けているんですね。

もう少し頑張ってみます。
もっとも僕にとっては、連休ではないのですがw

ちょっと楽しかったりします。
恐らく、最終的にギブするかもしれませんが^^;

# re: 簡単な実行中ウィンドウ(その3) 2007/02/13 11:03 R・田中一郎

すみません。ギブです。
ヘルプを何度も眺めたのですがわかりません。

>ほかにも、ShowDialog だからこそな、超有名なイベントがw
>よくコンストラクタと混同されてる質問を見かけますよw<最近減りつつあるけどww

上記から Load イベントだろうなー、という推測はついているのですが、一体 Load イベントで何をすべきなのかがわからないです(一番肝心な部分ですね^^;)

# re: 簡単な実行中ウィンドウ(その3) 2007/02/13 12:16 とっちゃん

すでに自身で書いてますが、Load イベントですよ。

ここで意識すべきは、どうやってウィンドウを「作っているか」です。
.NET の場合、明示的にウィンドウを作るという作業はあまりしませんが(CreateControlを使えば作成は可能)、ウィンドウはオブジェクトの作成と同時に生成されるわけではありません。
そこに着目できるか?がウィンドウを待機オブジェクトのインスタンスとして活用できるか?という点に集約されてきます。

今一度ShowDialogしたときのフォームの作成とその前後の流れを確認してみましょう。

ウィンドウオブジェクトを構築(フォームの実体であるウィンドウはこの時点では生成されていない)
ShowDialog の呼び出し
・オーナーウィンドウを無効化
・ウィンドウの作成(CreateControlに相当するものが呼ばれる)
・Loadイベント発生
・Showする
・Shownイベント発生
・ウィンドウがある間、DoEventsループを実行する
///Closeメソッドが誰かに呼ばれる(ボタンを押した結果もあり得る)
・Closingイベント発生
・ウィンドウを非表示にする
・Shownイベント発生
・Closed イベント発生
///Closeメソッド終了
・ループを抜ける
・オーナーウィンドウを有効化
ShowDialogの終了

という流れになります(・の部分は、ShowDialogの中身の擬似処理です)

このように、ShowDialog ではウィンドウを作って破棄するまで帰ってこないというのがその特性となります。
着目すべき点はいくつもあるのですが、一番注目しておきたいのが、ウィンドウはその中で作られるということですね。
だからこそ、Completed ではCloseメソッドを呼び出しているわけですし、ShowDialog を使ってメインスレッド自身待機状態とさせているわけです。

そして、もう一つ忘れてはいけないのは、スレッドはなるべく早く起動しておきたいということです。

なので、ShowDialog を使っての待機の場合、正解は Load イベントでということになります。

これが、Show メソッドで非同期処理という場合は、全然条件が違うので注意してくださいねw
「あくまでも」ShowDialog の場合だからということです。

#この辺がTips系の難しいところw

# re: 簡単な実行中ウィンドウ(その3) 2007/02/13 13:23 R・田中一郎

なるほどなるほど、ShowDialog を使うことで Load イベントが発生しますもんね。
素直に考えれば良かったんですね orz

お忙しいでしょうに、ご丁寧な解説をありがとうございました。
元記事も編集しておきます。

# re: 簡単な実行中ウィンドウ(その3) 2007/02/13 13:36 とっちゃん

もう。。めっちゃ大変っす。
3連休中家ではほとんどPC触ってなかったんで、未読が山のように...w<おい!
RSSはわんくまから消化してるんですが、今週は大阪勉強会があったから、まだワンくまだけですら半分しか...w

いそがしい、いそがしいww

# re: 簡単な実行中ウィンドウ(その3) 2007/02/13 13:42 R・田中一郎

>いそがしい、いそがしいww

大変ですねー^^;

# ygDArjlmQLWFPWUxb 2012/01/07 7:46 http://www.luckyvitamin.com/c-390-maca

Not bad post, leave it at my bookmarks!...

タイトル
名前
Url
コメント