- PR -

ShowDialogのようなメソッドの実装方法

1
投稿者投稿内容
re-guzy
会議室デビュー日: 2005/06/11
投稿数: 11
投稿日時: 2006-10-03 00:53
お世話になっております。
件名のことで現在悩み中です。

一般的なFormの表示はこのようになると思います。
コード:
using(Form1 f = new Form1()){
  f.ShowDialog();
}



Formを継承したクラスではない、通常のクラスで同じようなことは実現可能でしょうか?
つまり、ボタンのClickイベントハンドラで
コード:
using (MyClass c = new MyClass()) {
  c.Wait();//Waitメソッドは時間のかかる処理
}


というコードを実行した場合、
・WaitメソッドはUIスレッド以外のスレッドで実行される。
・Waitメソッド実行中もUIは応答する。
・Waitメソッド終了後、ボタンのClickイベントハンドラに制御が移る。
ということは実現できるのでしょうか?

ぱっと思いついた以下のコードでは、UIの応答性はよくありませんでした。
コード:
class MyClass : IDisposable {
  public void Wait() {
    //別スレッドを起動
  }
  public void Dispose() {
    while(this.IsWorking) {//別スレッド起動中はtrue
       Application.Doevents();
    }
  }
}

まどか
ぬし
会議室デビュー日: 2005/09/06
投稿数: 372
お住まい・勤務地: ますのすし管区
投稿日時: 2006-10-03 09:18
結局ShowDialogはさておき一般的なマルチレッドだと思いますので
Threadクラス、BackGroundWokerクラス(コンポーネント)についてお調べになるとよろしいかと。
「参考」
http://www.microsoft.com/japan/msdn/vs05/vbasic/threadinginvb2005.aspx
#あら、VB向けみたいなドキュメント

引用:

・Waitメソッド終了後、ボタンのClickイベントハンドラに制御が移る。


非同期で依頼をするので、後はその処理から通知を待つことになります。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2006-10-03 09:24
コード:

using (MyClass c = new MyClass()) {
c.Wait();//Waitメソッドは時間のかかる処理
}



なんてコードを書いてしまったら、UIがブロックするのは避けられません。

Windows の GUI は、メインスレッドでメッセージポンプが回ることで応答性を確保しているのですから、イベントハンドラにブロックするコードを書けば、UI がブロックするのは当然です。

UI の応答性を確保したいなら、「時間のかかる処理」は別スレッドで実行し、メインスレッドはその終了通知を待ち受けるような構造にしなくてはなりません。

GUI ベースのアプリケーションを書いているなら、BackgroundWorker クラスを使うのが妥当な線だと思います。

引用:

ぱっと思いついた以下のコードでは、UIの応答性はよくありませんでした。



VB6 で良く使われれていた手法ですね。

見た目に単純な解放ですが、実際には状態管理が異常に複雑化する場合があり得るので、個人的には避けるが吉と思います。


[ メッセージ編集済み 編集者: 渋木宏明(ひどり) 編集日時 2006-10-03 09:29 ]
わちゃ
大ベテラン
会議室デビュー日: 2005/12/05
投稿数: 162
お住まい・勤務地: 東京
投稿日時: 2006-10-03 12:53
みなさん、指摘されていますが、時間のかかる処理をそのまま別のスレッドで動かせばいいと思います。

イベント内には、

 Thread newThread = new Thread(new ThreadStart(this.DoWork));
 newThread.Start();

ってな感じで、書いてあげて、DoWork を

void DoWork() {
 MyClass c = new MyClass();
 c.HeavyWork();
 // その後の、処理
}

ってな感じで書いてあげればいいと思います。

実際の処理は、別のスレッドで動かしてやることで、イベントの処理をさっさと終わらせて、
別のスレッドの中で、その重い処理と、その後始末の処理をやってあげればいいはずです。

ただ、こんな感じでやると、そのボタンを連打した時に大変な事になる場合がある点に
注意が必要かと思います。

# VB 使いなんで、C# の文法が間違っていたら、すまんです。
re-guzy
会議室デビュー日: 2005/06/11
投稿数: 11
投稿日時: 2006-10-04 00:02
まどかさん、渋木宏明(ひどり)さん、わちゃさん回答ありがとうございます。

せっかく回答していただいたのに恐縮ですが、説明が足りなかったようです。
皆さん指摘されたように、時間がかかる処理を別スレッドにする、というのは理解しております。

実際には、MyClass#WaitメソッドはBackgroundWorkerを使用して時間のかかる処理を行っています。
このMyClassは解放が必要なリソースを使用していて、BackgroundWorker#RunWorkerCompletedイベントハンドラでMyClass#Disposeを呼んでいます。

ここまで実装して、ボタンのClickイベントハンドラで生成したのだから、そこでMyClassの破棄もやった方がわかりやすいのでは?と思いました。

1.起動トリガはボタンのClickイベントハンドラなどUIスレッドの処理
2.時間のかかる処理は別スレッドで行う。
3.別スレッドの処理が終了するとそのスレッドを起動した場所に制御が戻る。
4.その間にUIスレッドはブロックしない。
これらの条件を満たす方法があるのか?というのが質問です。
そこで似たようなコードになるShowDialogを引き合いに出して質問したわけです。
(2.が別フォームを表示するに対応するかと思います。)

できないならできないで、今のまま実装します。

ちなみに、Application.DoEventsは苦肉の策です。普段使用することはありません。
まどか
ぬし
会議室デビュー日: 2005/09/06
投稿数: 372
お住まい・勤務地: ますのすし管区
投稿日時: 2006-10-04 00:32
引用:

このMyClassは解放が必要なリソースを使用していて、BackgroundWorker#RunWorkerCompletedイベントハンドラでMyClass#Disposeを呼んでいます。


それでいいのでは?

引用:

ここまで実装して、ボタンのClickイベントハンドラで生成したのだから、そこでMyClassの破棄もやった方がわかりやすいのでは?と思いました。


非同期呼び出しですので以降のステップがすぐに実行し続けますよね。
なのでそこに書きたくても書けないと思うのですが。

[/quote]
そこで似たようなコードになるShowDialogを引き合いに出して質問したわけです。
(2.が別フォームを表示するに対応するかと思います。)
[/quote]
これは単に同期呼び出しで別スレッドではないのでは?
re-guzy
会議室デビュー日: 2005/06/11
投稿数: 11
投稿日時: 2006-10-04 22:34
まどかさん、回答ありがとうございます。

引用:

非同期呼び出しですので以降のステップがすぐに実行し続けますよね。
なのでそこに書きたくても書けないと思うのですが。


自分もそう思うのですが、もしかしたら方法があるのでは、と思って質問しました。

引用:

これは単に同期呼び出しで別スレッドではないのでは?


同期呼び出しなんでしょうか?
ShowDialogが同期呼び出しなのであれば、UIスレッドはブロックされているはず。
しかし、別スレッドからUIスレッドに処理を委譲すると(Control#Invoke)きちんと処理されます。
そこがいまいち納得できてないわけなんです。

とりあえず最初の質問のようなことは無理そうなので、今の方法で実装します。
ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)