■ はじめに
https://dk521123.hatenablog.com/entry/2010/12/12/164101
https://dk521123.hatenablog.com/entry/2010/12/25/221009
https://dk521123.hatenablog.com/entry/2010/10/22/101350
の続き。 今回は、処理が終わったら、 コールバック関数を呼び出して実行結果を得る
■ 使用上の注意
* メインスレッドではなく、コントロールを作成していない別スレッドから コントロールに直接呼び出しはできない => 以下のような「例外内容」が発生する
例外内容
System.InvalidOperationException: '有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'label3' がアクセスされました。'
参考文献
http://cammy.co.jp/technical/2017/04/25/c-%E3%82%B5%E3%83%96%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%8B%E3%82%89%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%AE%E8%A1%A8%E7%A4%BA%E3%82%92%E8%A1%8C%E3%81%86invoke/
公式サイト
https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls
■ サンプル
例1
呼び出し側
using System; using System.Runtime.Remoting.Messaging; using System.Windows.Forms; namespace SampleForm { // 呼び出し側 public partial class Form1 : Form { // 非同期実行するためのデリゲート private delegate string SampleDelegate(int sleep, string format); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { HeavyTask heavyTask = new HeavyTask(); // 実行するデリゲートを作成 SampleDelegate sampleDelegate = new SampleDelegate(heavyTask.DelegatingMethod); // コールバック関数 AsyncCallback callback = new AsyncCallback(this.CallbackMethod); this.label1.Text = "Before calling BeginInvoke()"; // 非同期実行の呼び出し IAsyncResult async = sampleDelegate.BeginInvoke(5000, "yyyy/MM/dd", callback, null); this.label2.Text = "Called BeginInvoke()"; } // コールバック関数:スレッド終了後の処理を記述 private void CallbackMethod(IAsyncResult async) { // AsyncResultに変換 AsyncResult asyncResult = async as AsyncResult; // 非同期の呼び出しが行われたデリゲート オブジェクトを取得 SampleDelegate sampleDelegate = asyncResult.AsyncDelegate as SampleDelegate; // 処理結果取得 this.Invoke(new MethodInvoker( delegate { string result = sampleDelegate.EndInvoke(async); this.label3.Text = "Today is " + result; })); // 以下の処理だと「this.label3.Text = "Today is " + result;」で例外発生 // string result = sampleDelegate.EndInvoke(async); // this.label3.Text = "Today is " + result; } } }
呼び出され側
using System; namespace SampleForm { public class HeavyTask { // 非同期させたい(重たい)処理 public string DelegatingMethod(int sleep, string format) { System.Threading.Thread.Sleep(sleep); return DateTime.Now.ToString(format); } } }
出力結果
【ボタン押下直後】 Before Calling BeginInvoke() label2 label3 【ボタン押下5秒後】 Before Calling BeginInvoke() Called BeginInvoke() Today is 2019/03/09
例1の別解
using System; using System.Runtime.Remoting.Messaging; using System.Windows.Forms; namespace SampleForm { // 呼び出し側 public partial class Form1 : Form { // 非同期実行するためのデリゲート private delegate string SampleDelegate(int sleep, string format); private delegate void SetLabelTextForResultDelegate(string result); private SetLabelTextForResultDelegate delegateForThreadSafe; public Form1() { InitializeComponent(); this.delegateForThreadSafe = new SetLabelTextForResultDelegate(this.SetLabelText); } private void button1_Click(object sender, EventArgs e) { HeavyTask heavyTask = new HeavyTask(); // 実行するデリゲートを作成 SampleDelegate sampleDelegate = new SampleDelegate(heavyTask.DelegatingMethod); // コールバック関数 AsyncCallback callback = new AsyncCallback(this.CallbackMethod); this.label1.Text = "Before calling BeginInvoke()"; // 非同期実行の呼び出し IAsyncResult async = sampleDelegate.BeginInvoke(5000, "yyyy/MM/dd", callback, null); this.label2.Text = "Called BeginInvoke()"; } // コールバック関数:スレッド終了後の処理を記述 private void CallbackMethod(IAsyncResult async) { // AsyncResultに変換 AsyncResult asyncResult = async as AsyncResult; // 非同期の呼び出しが行われたデリゲート オブジェクトを取得 SampleDelegate sampleDelegate = asyncResult.AsyncDelegate as SampleDelegate; string result = sampleDelegate.EndInvoke(async); this.Invoke(this.delegateForThreadSafe, new object[] { result }); } private void SetLabelText(string result) { this.label3.Text = "Today is " + result; } } }
関連記事
Delegate / Event ~ 入門編 / Delegate ~
https://dk521123.hatenablog.com/entry/2010/12/12/164101
Delegate / Event ~ 入門編 / Event ~
https://dk521123.hatenablog.com/entry/2010/12/25/221009