bxdxmx3

きじれてじろあ なきがせすで あぷせとねでぶ

WindowsFormsとWPFの親子画面でのスレッドの取り扱い方

画面間での排他制御処理をしようとして、ちょっとはまったので
画面絡みのスレッド扱いについてまとめてみる。

親画面と子画面のスレッドの扱い

Windows Fomsの場合

親画面のボタンをクリックすると、子画面をつくり表示するだけの単純なもの。
画面では自分のスレッド番号を表示している。

Form1()
{
    InitializeComponent();
    label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
}
void button1_Click(object sender, EventArgs e)
{
    new Form2().Show();
}
Form2()
{
    InitializeComponent();
    label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
}

結果はこうなる。
f:id:eo-oe-aaaa:20090703101758j:image
子画面は親画面のスレッドと同じスレッドで動作していることがわかる。

WPFの場合

コードはWindowsFormsとほぼ同じ。

Window1()
{
    InitializeComponent();
    label1.Content = Thread.CurrentThread.ManagedThreadId.ToString();
}
void button1_Click(object sender, RoutedEventArgs e)
{
    new Window2().Show();
}
Window2()
{
    InitializeComponent();
    label1.Content = Thread.CurrentThread.ManagedThreadId.ToString();
}

結果はこうなる。
f:id:eo-oe-aaaa:20090703101759j:image
同じく、子画面は親画面のスレッドと同じスレッドで動作していることがわかる。

困った。

各画面間で排他制御をしたい場合、
Monitor.Enterなどでロックを取得するが、
スレッドが同じ場合ロックをすり抜けてしまう。

対策

だったら、子画面を別スレッドで生成すればいいんじゃない?
ってことでやってみたのが以下。

Windows Fomsの場合

Form2生成部分を以下のように変える。

void button1_Click(object sender, EventArgs e)
{
    new Thread(new ThreadStart(delegate()
    {
        new Form2().ShowDialog();		// Show()だとスレッド終了と共にWindowが消えてしまう。
    })).Start();
}

結果はこうなる。
f:id:eo-oe-aaaa:20090703101800j:image
見事に各画面のスレッドが別になったので、
画面毎の処理で排他制御ができるようなった。

WPFの場合

コードはWindowsFormsとほぼ同じ。

void button1_Click(object sender, RoutedEventArgs e)
{
    new Thread(new ThreadStart(delegate()
    {
        new Window2().Show();
    })).Start();
}

結果は・・・・
f:id:eo-oe-aaaa:20090703101801j:image
WPFでは親画面とは別のスレッドでは、ウインドウ生成ができない!

どうしても別スレッドからウインドウの生成をしたい場合には?

親画面のスレッドで処理されるようにInvokeしてやればよい。
Dispatcher.CheckAccess()で自スレッドかチェックを行い、
違う場合はInvokeしてやる。

void button1_Click(object sender, RoutedEventArgs e)
{
    new Thread(new ParameterizedThreadStart(delegate(Object obj)
    {
        Window1 w = obj as Window1;
        if (!w.Dispatcher.CheckAccess())
	{
	    w.Dispatcher.Invoke((MethodInvoker)delegate()
	    {
		new Window2().Show();
	    });
	}
	else
	{
	    new Window2().Show();
	}
    })).Start(this);
}

結果は・・・・
f:id:eo-oe-aaaa:20090703101759j:image

WPFではどうやっても子画面を別スレッドで動かすことはできないです。。
違う方法で排他制御をしなきゃだめってことですね。