Control.Invokeを使わない別スレッドからのコントロールのアクセス

フォームコントロールは別スレッドからのおさわり厳禁なんで、アクセスするときはcontrol.Invokeメソッドを使わなければなりません。詳しくは:
Windowsフォームで別スレッドからコントロールを操作するには?
http://www.atmarkit.co.jp/ait/articles/0506/17/news111.html

例えば、フォームコントロールのアップデートをスレッドセーフにするには

private void TextUpdate(Control control, string text)
{
Action act = () =>
{
control.Text = text;
};
if(control.InvokeRequired)
{
control.Invoke(act);
}
else
{
act();
}
}

のように書くのが一般的かと思います(あるいはデリゲートを定義してコールバックを使う)。ただ、これ結構コードが冗長になります

実はこれタイマーを使うとうまく解決できるのではないかと思います。タイマーと言ってもいくつかあるのですが、使うのは2つ。

(1) System.Windows.Forms.Timer
(2) System.Timers.Timer


主な違いは、
(1) : System.Windows.Forms.Timer
・コントロールのあるスレッドで必ず実行される(スレッドセーフ
・同期的に処理されるので、実行中の入力はロックされる
・Intervalが極端に低い場合は精度が低く、高頻度なアップデートには適さない
(2) : System.Timers.Timer
・別スレッドで実行される(スレッドセーフではない)。ただし、Timer.SynchronizingObjectにForm等のコントロールを与えることで、BeginInvokeと同等の処理が行われる(既存のコードの改修なしでスレッドセーフにできる)
・非同期的に処理されるので、実行中の入力はロックされない
・Forms.Timerより精度は高い

他にもSystem.Threading.Timerがありますが、これをフォームコントロールで使う理由はありません。素直にSystem.Timers.Timerを使いましょう。

実装は次のようにします。

(1) Queue<Action>のように、コントロールの更新用のキューを用意する(キューのforeach等の列挙操作はスレッドセーフではないものの、Dequeue, Enqueueはスレッドセーフが保証されているので)
(2) コントロールにアクセスして値を書き換える際に、処理を(1)のキューに放り込む
(3) タイマーから定期的に(1)から読み込んで実行

System.Timers.Timerを使った例。ボタンを押すと100msごとにラベルの値が変わります。Forms.Timerはほとんど変わらないので読み替えてください。Forms.Timerの場合は、どんな違うスレッドからキューを追加しても、処理はコントロールのあるスレッドで行われるのでInvokeは発生しない(はず)。UIのロックに目を瞑れば結構速いです。

public partial class Form1 : Form
{
System.Timers.Timer timer;
Random rnd;
Queue ctlQueue;

public Form1()
{
InitializeComponent();

ctlQueue = new Queue();

rnd = new Random(Environment.TickCount);

timer = new System.Timers.Timer();
timer.Interval = 100;
timer.SynchronizingObject = this;
timer.Elapsed += timer_Elapsed;
timer.Start();
}

void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if(ctlQueue.Count > 0)
{
ctlQueue.Dequeue()();
}
}

//キューに処理を放り込む
private void button1_Click(object sender, EventArgs e)
{
foreach(var i in Enumerable.Range(0, 100))
{
Action act = () =>
{
label1.Text = rnd.Next(0, 100).ToString();
};
ctlQueue.Enqueue(act);
}
}
}


(多分本来のタイマーの使い方じゃない)
スポンサーサイト
プロフィール

こしあん

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

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク