C#で、DataGridViewを作成したスレッド以外からColumn追加しようとするとこんなエラーが出ました。
有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'dataGridView2' がアクセスされました。
マルチスレッドの観点からスレッドセーフでない呼び出しを行うとこのようなエラーが出るようです。
Javaだとよくロックして困ったのですが、C#ではエラーがわかりやすいです。
C# 簡単なスレッドセーフでない呼び出しのサンプリアプリケーション
Formにボタンとテキストボックス2個を追加しただけの簡単なサンプルを考えます。
ボタンを押した後、別のスレッドでテキストボックスをフォーカスしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; namespace WindowsFormsApplication14 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(new ThreadStart(anotherThread)); t.Start(); } void anotherThread() { textBox2.Focus(); } } } |
実行するとこんなエラーが出ます。
型 'System.InvalidOperationException' のハンドルされていない例外が System.Windows.Forms.dll で発生しました
追加情報: 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'textBox2' がアクセスされました。
コントロールを作成したスレッド以外から、このコントロールをフォーカスすることは許されないようです。
C# フォームコントロールのスレッドセーフな呼び出し方法
Invoke(new delegate())を使って、上記のプログラムを書き換えてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; namespace WindowsFormsApplication14 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, System.EventArgs e) { Thread t = new Thread(new ThreadStart(anotherThread)); t.Start(); } delegate void delegate1(String text1); void setFocus(String text1) { textBox2.Text = text1; textBox2.Focus(); } void anotherThread() { Invoke(new delegate1(setFocus), "AAA"); } } } |
delegate void delegate1()
というメソッドはスレッドセーフな呼び出しが可能となりました。
引数も取ることが可能です。
まとめ
いかがでしたでしょうか?
スレッドセーフなプログラミングは、C++でもJavaでもいつも悩ましかったのです。
JavaのGUIコンポーネントであるSwingはシングルスレッド設計であり、Swing は常に一つのスレッドからのみアクセスすることを意識しないといけませんでした。
そのため、Javaでちょっと凝ったことをするとよくロックしてしまっていたのですが、C#は実行時にわかりやすくエラーが出る上に、
Invoke(new delegate())
をすれば、そのスレッドで動かすことが可能です。
プログラミングの無料レッスン体験
約8,000名の受講生と80社以上の導入実績のあるプログラミングやWebデザインのオンラインマンツーマンレッスンCodecamp
<Codecampの特徴>
1 現役エンジニアによる指導
2オンラインでのマンツーマン形式の講義
3大手企業にも導入されている実践的なカリキュラム
↓無料体験レッスン実施中です。
コメント