.netのBeginInvokeにはデリゲート標準とコントロールの実装があるのでわかりずらいので整理
・delegateのBeginInvoke
BeginInvokeは非同期で実行する。登録できる関数は1個で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 33 34 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace rawdele { class Program { delegate void mydelegate(); static void myfunc() { Console.WriteLine("myfunc Thread:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(500); Console.WriteLine("myfunc1"); } static void Main(string[] args) { Console.WriteLine("Main Thread:" + Thread.CurrentThread.ManagedThreadId); mydelegate myd = new mydelegate(myfunc); Console.WriteLine("Normal Call"); myd(); Console.WriteLine("Invoke Call"); myd.Invoke(); Console.WriteLine("BeginInvoke Call"); IAsyncResult iar = myd.BeginInvoke(null, null); Console.WriteLine("Wait BeginInvoke Call"); myd.EndInvoke(iar); } } } |
・ControlのBeginInvoke
ControlのBeginInvokeはWin32のPostMessageみたいなもので別スレッドでは実行しない。一般的にControlはそれを作ったスレッドしか触れないのでマルチスレッドで別スレッドからcontrolにアクセスするときは使う。PostMessageとは言っても引数を渡したり、あとで待ったりできるので高機能。
Form1.cs
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace forminvoke { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { this.Text += "ID:" + Thread.CurrentThread.ManagedThreadId; } Thread _t; Form2 _f2; void ths() { _f2=new Form2(); _f2.ShowDialog(); _f2.Dispose(); } private void button1_Click(object sender, EventArgs e) { _t = new Thread(ths); _t.Start(); } private delegate void invokedele(); private void invoked() { MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); } private void button2_Click(object sender, EventArgs e) { this.BeginInvoke(new invokedele(invoked)); } private void button3_Click(object sender, EventArgs e) { _f2.BeginInvoke(new Form2.invokedele(_f2.invoked)); } private void button4_Click(object sender, EventArgs e) { Class1.threadsafemethod(this); Class1.threadsafemethod(_f2); } } } |
Form2.cs
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 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace forminvoke { public partial class Form2 : Form { public Form2() { InitializeComponent(); } private void Form2_Load(object sender, EventArgs e) { this.Text += "ID:" + Thread.CurrentThread.ManagedThreadId; } internal delegate void invokedele(); internal void invoked() { MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); } } } |
Class1.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace forminvoke { class Class1 { delegate void dele(Control c); internal static void threadsafemethod(Control c) { if (c.InvokeRequired) { c.BeginInvoke(new dele(threadsafemethod), c); return; } MessageBox.Show("threadsafemethod" + Thread.CurrentThread.ManagedThreadId); } } } |
form1は別スレッドでform2を作る。_f2にはform1のスレッドからは触れないのでBeginInvokeまたはInvokeを呼ぶことになる。関数はForm1にあってもいい。InvokeRequiredを使えば、Invokeが必要か判断できる。ただし多用すると、簡単にデッドロックになるだろう。このソースは本来form2を作るときもっと慎重にやらないとならないがスルーしてる。