- - PR -
【C#】別スレッドからのFormコントロール操作
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2005-11-07 14:14
いつもお世話になっております。
別スレッドからのFormコントロール操作に関して教えてください。 下記のようなケースはどのように実装するのが良いのでしょうか? 調べた限りでは、下記案がありそうですが、 案1,2では、ワーカスレッドの処理完了にForm側が同期してしまいNGです。 また案3は、CallBack内でFormコントロールを操作させたとして スレッドセーフなのか?という疑問を持っています... 案1:Control::Invoke()メソッドを使用 案2:Control::BeginInvoke()/EndInvoke()メソッドを使用 案3:Control::BeginInvoke()/EndInvoke()メソッドを使用しつつ、 CallBack処理で実装する。 ●処理シーケンス @ユーザが、Form上のボタンクリック ↓ Aボタンクリック関数内で、ワーカスレッドを生成し重い処理を実施する ↓ Bボタンクリック関数を抜ける。 |※Formとしては、処理を何もしていない。 ↓ Cワーカスレッドで処理が完了する。 ↓ Dワーカスレッドの実行結果を、Form上に表示する。 (別スレッドからのコントロール操作) やりたいことのポイントですが、 ・ワーカスレッドの実行完了を待たずに、Formは処理を完了する。 (ユーザから見て、Formをブロックさせたくない。) ついでですが、上記案1〜3では、ワーカスレッド側でメッセージボックスを 表示すると、(Formスレッド側で実施していないので、) メッセージボックスがFormの裏側に回ってしまったりします。 これって、どうしようもないのでしょうか? C++でのPostMessage系の処理って、ないのでしょうか? 以上、よろしくお願いいたします。 | ||||||||
|
投稿日時: 2005-11-07 15:58
こんにちは。
Windowハンドルをもったコントロールを操作するには、操作する瞬間だけでもWindowハンドルをもったスレッドと同期させる必要があるので、完全に非同期で画面の内容を書き換えることはできません。 この場合、(5)の処理も重いんでしょうか? (例えば、数千件のレコードをリスト表示するとか) もし(5)の処理をループで回して書き換えているのであれば、ループの中にDoEventsを記述してやれば(もち、Windowハンドルをもったスレッド側)、キューに溜まったメッセージを処理するので、非同期で動いているようにみせることはできます。それじゃ、だめ? | ||||||||
|
投稿日時: 2005-11-07 16:24
以前私が立てたスレッドでは、
・別スレッドの処理を別のクラス(スレッドクラス)に分離 ・スレッドクラスで、SynchronizingObjectプロパティを実装 ・スレッドの処理が完了したら、イベントを発生させる ->イベント内で、SynchronizingObjectが設定されていたらInvoke ・スレッドからのメッセージは、イベント変数に格納 ・フォーム側でイベントを処理(メッセージボックスを表示、など) という形になりました。 VBですが、こんな感じでいかがでしょうか。
| ||||||||
|
投稿日時: 2005-11-07 17:27
みなさま回答ありがとうございます。
DoEventはFormの処理が重くなるらしい!?ようなので、 できれば使用を避けたいと考えています。 またVB.Netは、イベントまでいってしまうと、 正しく読めている自信がありません... (ずっと、C++、C#で開発だったもので...、でも非常に参考になっています。) ちなみに、回答を頂いている間に下記のようなコードを書いてみました。 (Invokeで実行した処理は、Form側のスレッドで処理されているようです。) ただ、『ワーカスレッド上からForm::Invoke関数を呼出しても安全なのか』 ということが引っかかっています。 これでいいのでしょうか? parent.Invoke(new SetFocusDelegate(parent.SetFocus)); ●サンプルソース //Invoke用のデリゲート delegate void SetFocusDelegate(); //フォームクラス public class Form1 : Form { public void SetFocus() { ////////////////////////////////////// //安全に画面操作を実施できるはず!?// ////////////////////////////////////// MessageBox.Show("Thread Completed..."); } private void button4_Click(object sender, System.EventArgs e) { myThread my = new myThread(this); Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(my.Run)); th.Start(); MessageBox.Show("Form Completed..."); } } //ワーカスレッドのクラス public class myThread { public Form1 parent = null; public myThread(Form1 p) { parent = p; } public void Run() { //重い処理 System.Threading.Thread.Sleep(5000); parent.Invoke(new SetFocusDelegate(parent.SetFocus)); } } 以上、お忙しいとは思いますが、よろしくお願いいたします。 | ||||||||
|
投稿日時: 2005-11-08 02:29
大丈夫です。 Form::InvokeやBeginInvokeはフォームを作ってないスレッドから呼び出すためにあります。 EndInvokeはフォームを作ったスレッドでも、他のスレッドでも呼び出します。
BeginInvokeがそれです。 内部でメッセージポンプを使って 違うスレッドにメッセージをおくってます。 ちなみに、Makotoさんのように、ワーカースレッドのクラスでフォームの関数をInvokeを呼び出すのもありだと思いますが、 私はkanaiさんのようにワーカースレッドのクラスにイベントを作ることが多いです。 ワーカースレッドクラスの再利用がやりやすいんですよね。 | ||||||||
|
投稿日時: 2005-11-08 12:01
回答ありがとうございます。
おかげさまで自信をもって実装できそうです。 >ちなみに、Makotoさんのように、ワーカースレッドのクラスでフォームの関数を >Invokeを呼び出すのもありだと思いますが、 >私はkanaiさんのようにワーカースレッドのクラスにイベントを作ることが多いです。 >ワーカースレッドクラスの再利用がやりやすいんですよね。 ところで、『ワーカスレッドクラスの再利用』って、どういうことでしょうか? 私の認識では、『ワーカスレッド』=『特定の処理を実施するスレッド』 なので抽象化して再利用性が上がるとイメージできないのですが...? (各種イベントを識別することで、一つのスレッドクラスですべて処理する ⇒再利用性向上ということでしょうか?) ↑これは実装し続けていれば、いずれ気付けることなのかも知れませんね。 今後も勉強していきます。 以上、アドバイスありがとうございました。 | ||||||||
|
投稿日時: 2005-11-08 13:32
どちらかというと、 「ワーカースレッドを活用するような機能」の、コンポーネントとしての再利用性向上 というような意味合いではないですかね。 ※スレッドを利用するような「機能」を、部品として扱いやすい形のクラスとして実装する みたいな。 | ||||||||
|
投稿日時: 2005-11-08 17:23
『ワーカスレッド』=『特定の処理を実施するスレッド』 ですので、コマンドラインアプリケーションでの利用や、 Windowsのサービスとして実行したい場合もあるかもしれません。 その場合、ワーカースレッドクラスがFormを必要とすると、 そのままでは使えません。 kanaiさんの実装方法なら、SynchronizingObjectを設定しなければそのまま使えます。 イベントをつかうのも同じです。 スレッドから呼ばれるFormでコールバック用のメソッドを 毎回定義するのはめんどうです。 古いコードの使い方って、すぐ忘れてしまいますから。 たとえば、そんな話です。 |