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(); }
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(); }
困った。
各画面間で排他制御をしたい場合、
Monitor.Enterなどでロックを取得するが、
スレッドが同じ場合ロックをすり抜けてしまう。
対策
だったら、子画面を別スレッドで生成すればいいんじゃない?
ってことでやってみたのが以下。
Windows Fomsの場合
Form2生成部分を以下のように変える。
void button1_Click(object sender, EventArgs e) { new Thread(new ThreadStart(delegate() { new Form2().ShowDialog(); // Show()だとスレッド終了と共にWindowが消えてしまう。 })).Start(); }
WPFの場合
コードはWindowsFormsとほぼ同じ。
void button1_Click(object sender, RoutedEventArgs e) { new Thread(new ThreadStart(delegate() { new Window2().Show(); })).Start(); }
結果は・・・・
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); }
WPFではどうやっても子画面を別スレッドで動かすことはできないです。。
違う方法で排他制御をしなきゃだめってことですね。