- - PR -
(C#2.0)別スレッドからフォームを制御
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-03-14 13:34
よこけんさんがおっしゃってますが、イベントを発生させてもイベントハンドラはその別スレッドの方で実行されますので、「別スレッドからの処理状況通知」には使えないんですよね。 解決策としては、Invokeでデリゲートを呼び出すメソッドを実装し、スレッドメソッド() 内でイベントを発生させる代わりにそれを呼び出すように変更するのが良いかと思います。 どうしても、イベントを発生させた時に別スレッドでイベントハンドラを実行したいというなら、方法が無いわけでもないんですが…。といってもC#じゃ無理ですけど。
C++/CLIです。C#ではイベントにaddとremoveアクセッサしか定義できませんが、C++/CLIではその他raiseアクセッサといってイベントが発生した時に実行されるアクセッサ関数を定義できます。 ここでInvokeしてやれば、コントロールを保有しているスレッドでイベントハンドラが実行されます。 [ メッセージ編集済み 編集者: 一郎 編集日時 2007-03-14 13:35 ] | ||||||||
|
投稿日時: 2007-03-15 09:22
一郎さん回答ありがとうございます。
とても勉強になります^^ ひとつ教えて頂きたいのですが、 下記のようなやり方で、メインプロセスにイベントを発生させるやり方は、何か問題が ありますか?「おっできた」って感じで使っちゃっていたのですが^^; 簡略版ですが、以下のような感じです。 public class Form1 : System.Windows.Forms.Form { private ThreadClass[] mThread = new ThreadClass[5]; private void Form1_Load(object sender, System.EventArgs e) { for(nIdx = 0; nIdx < 5; nIdx++) { this.mThread[nIdx] = new ThreadClass(); this.mThread[nIdx].ThreadName = "Thread" + nIdx.ToString(); this.mThread[nIdx].SetLabel += new ThreadClass.SetLabelEventHandler(OnSetLabel); this.mThread[nIdx].Run(); } } private void OnSetLabel(SetLabelEventArgs e) { Label1.text = e.SetString; } } public class ThreadClass { private string msThreadName; public string ThreadName { get{return this.msThreadName;} set{this.msThreadName = value;} } // SetLabelイベント public delegate void SetLabelEventHandler(SetLabelEventArgs e); public event SetLabelEventHandler SetLabel; protected virtual void OnSetLabelEvent(SetLabelEventArgs e) { if (SetLabel != null) SetLabel(e); } // スレッドの実行 public void Run() { System.Threading.Thread thread; try { thread = new System.Threading.Thread(new ThreadStart(ThreadMethod)); thread.Start(); } catch { } } private void ThreadMethod() { this.SetLabel(new SetLabelEventArgs("AAAAAA"); } } ご教授の程、宜しくお願い致します。 [ メッセージ編集済み 編集者: 紫苑 編集日時 2007-03-15 09:27 ] | ||||||||
|
投稿日時: 2007-03-15 11:25
こんにちは。
”イベント”って何を指しているか曖昧ですが、 提示されたコードでは、メインスレッドでイベントが起きていない (イベントハンドラがメインスレッドで実行されていない)ようですが。 正常動作しているように見えるのも、偶々だと思います。 ワーカースレッドからControl.Textプロパティへのセット処理が ネイティブのSetWindowText関数/WM_SETTEXTメッセージを経由することにより メインスレッド側で処理されたんでしょう。 | ||||||||
|
投稿日時: 2007-03-15 14:04
紫苑さんのソースを動作させてみたところ、VisualStudio2005のデバッグ実行をすると 「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外の…」 というメッセージのInvalidOperationExceptionが出ました。 しかし、ビルドしたexeを単体で実行すると、確かに"AAAAAA"が画面に出ましたね。 紫苑さんの環境でもそうですか?VisualStudio2005のデバッグ実行では例外が出ますよね? http://msdn2.microsoft.com/ja-jp/library/ms171728(VS.80).aspx ここに、
と書かれています。 動作環境や現在のフレームワークの実装の仕方等によりたまたま動いているだけですので、お客様の所に持って行ったら動かないとか、パッチを当てたら動かないとか、あるいは紫苑さんの環境で明日は動かないかもしれません。 例外が出ないからこそプログラマが気を付けてコーディングしないといけませんね。 | ||||||||
|
投稿日時: 2007-03-15 15:24
お世話になっております。
一郎さんがおっしゃるとおりの現象が起こったので このスレッドを作成しました^^;(最初の投稿を見てください) 最初から、ここまで公開して質問するべきでした。申し訳ありません。 Tdnr_Symさん、回答ありがとうございます。 勘違いをしていました・・・ 最初の投稿の内容に戻るのですが、このような作りの場合で Invokeを使用して、「有効ではないスレッド間の操作」を回避する 方法はあるのでしょうか? 一郎さんが以前におっしゃったやり方で、とりあえずは例外の回避ができたのですが、 問題があるのか無いのか判断ができずにいます・・・
[ メッセージ編集済み 編集者: 紫苑 編集日時 2007-03-15 15:25 ] | ||||||||
|
投稿日時: 2007-03-15 15:59
と書いてますが・・・。 | ||||||||
|
投稿日時: 2007-03-15 17:02
イベントを使っている理由が分かりました。紫苑さんは
「別スレッドから処理状況通知」 っておっしゃってましたが、正確には 「別クラスから(画面クラスへ)の処理状態通知」 ってことですよね。で、その別クラス(ThreadClass)がスレッドを生成しているということでしょう。 これはイベントを使ってもいい場面だと思います。 初め提示されたソースでは、フォームの中でスレッドを作ったりイベントを発生させたりしているように見えたので「イベントを使う場面じゃないよなぁ」と思ってました。 イベントというのは基本的に発生時にどのメソッドが動くかは発生元自身は意識しないものですので、今回の例でもThreadClassの方でInvokeをするのはおかしい、といいたい所ですが、別スレッドを作ってどうのこうのとしているのはThreadClassの方ですので、スレッドを扱っているThreadClassの方が対応するべきだ、という考え方もあるかと思います。 私だったら、ThreadClass.SetLabelイベントの説明に、 「このイベントは新しいスレッド上で発生する可能性があります。このイベントハンドラでフォーム等のコントロールを扱いたい場合はそれを所有しているスレッド上で行うようにしてください。」 などと書いておいて、画面クラス上のSetLabelイベントハンドラでInvokeする……かなぁ。 ちょっと今回は確信が持てないですね。これでいいのかどうか。 コード書いてみました。
匿名メソッドなどをふんだんに使用した贅沢な一品です。ご堪能ください。 (せっかくのC#2.0ですからね。そういえばこのスレッドのタイトルC#2005ってなってますけど、C#2005ではなくてC#2.0ですね。VBはVB2005ですけど。) [ メッセージ編集済み 編集者: 一郎 編集日時 2007-03-15 17:10 ] | ||||||||
|
投稿日時: 2007-03-15 17:38
お世話になっております。
一郎さん、レスありがとうございます。
別クラスか・・・そうですね、説明が『不十分+間違って』いたんですね・・・ ご迷惑をおかけしました。
参考にさせて頂きます。本当にありがとうございます^^ (タイトルって修正できないですよね・・・) |