スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Control.Invokeとの上手いつきあい方

他のスレッドからコントロールを操作する際に、例えば、

Task.Factory.StartNew(() =>
{
label.Text = "hoge";
});

とかやると、InvalidOperationExceptionが飛んで怒られます(前提)。これを回避するためには、Control.InvokeやBeginInvokeで元のスレッドに戻した上で実行させます。

ところがこのInvokeの処理は結構コストが大きいようで、

参考:Invoke はどれほど遅いのか?
http://blog.ume108.mobi/?p=1654

例えばLabelを2、30個ぐらい並べてforループさせて個々のLabelごとにInvokeしていると、その重さが目に見てわかるレベルになってしまいます。どのようになるかというと、ループした順にさーっと更新されていくのがわかるぐらい。ちらついて気になるといえば気になる。

MSDNの説明読むとそういう使い方でいいんじゃないのって勘違いするようなところもあって、

参考:方法 : Windows フォーム コントロールのスレッド セーフな呼び出しを行う
https://msdn.microsoft.com/ja-jp/library/ms171728%28v=vs.110%29.aspx


private void SetText(string text)
{
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}


(これよく見るとthis.Invokeであって、this.textBox1.Invokeではないんですよね…) 例えばこのメソッドをstaticにして引数にコントロールを与えると、なんでもInvokeできるスレッドセーフなプロパティ変更ラッパーメソッドができてしまうのですが、便利だからってそれをぐるぐるループで呼び出してるとさっきみたいなことになってしまいます(ついさっきまで自分がやってた)。

さっきみたいなLabelがたくさんある状態でやるなら例えば、

MethodInvoker invoker = delegate()
{
userControl11.label1.Text = "hoge";
userControl11.label2.Text = "huga";
userControl11.label3.Text = "piyo";
/// LabelのText変更 …
};


Task.Factory.StartNew(() =>
{
if(userControl11.InvokeRequired)
{
userControl11.Invoke(invoker);
}
else
{
invoker.Invoke();//ここは呼ばれないけど
}
});


MethodInvokerを別のメソッドで定義したいなら、先ほどのTextBoxの例のようにdelegateを用意して定義します。Invokeさせたコントロールに属するコントールは、Invokerの中では全てスレッドセーフにアクセスできるようです(PanelやUserControl等でネストしていてもOK)。

つまり、コントロールがいっぱいあるような状態で他のスレッドからアクセスするには、

・Control.Invokeを使う(大前提)
・個々のコントロールをちまちまInvokeさせるのではなく、親のコントロール(Form、UserControl)を1回どかんとInvokeさせて、そのInvokerの中でアクセスするコントロールを同じスレッドから呼びだすときのようにしたほうが速い(abel1.Text = "hoge"のように)

えっ常識だった?(´・ω・`)そんなー
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。