Last Updated 2011/09/21
SynchronizationContext クラスは、.NET Framework 2.0 に時代からありますが、.NET Framework 4.0 では ThreadPool クラスの大幅な強化にともなって、SynchronizationContext クラスの内容を変わったものと想像します。ただし、ブラックボックスになっていますので、詳しいことは分かりません。.NET Framework 4.0 の SDK でもこのクラスに対する記述は少ないし、コード例もほとんどありません。
並列処理について研究していると、SynchronizationContext クラスの重要性を認識するようになりましたので、何か参考になるものはないかと WEB サイトをうろついていると、これはと思う記事を見つけました。
記事のタイトルは、"Understanding SynchronizationContext (Part I)" ですが、残念ながら WEB サイトは不明です。ともあれ、この記事の一部を要約すると次のようになります。
このクラスはスレッドの枠を超えて、通信するための仕組みである。たとえば、スレッド1で実行中の内容をスレッド2に反映しようとする場合、スレッド1の同期コンテキストオブジェクトをスレッド2に渡すことで可能になる。スレッド2から同期コンテキストオブジェクトの Send または Post メソッドを呼び出せば、それはスレッド1上で実行した場合と同じになる。
この説明をコードを使って補足します。フォームに button1 と textBox を配置し、button1 をクリックすると、別のスレッドを起こしてデリゲートを実行し、その結果を textBox に表示します。
private void button1_Click(object sender, RoutedEventArgs e) { int id = Thread.CurrentThread.ManagedThreadId; Debug.WriteLine(String.Format("button1_Click ThreadId = {0}", id)); // UI スレッドの同期コンテキストオブジェクトを取得する var context = SynchronizationContext.Current; // 別のスレッドを起こす var thread = new Thread(StartProc); thread.Name = "* 別スレッド *"; // スレッドの実行を開始する(UI スレッドの同期コンテキストオブジェクトを引数として渡す) thread.Start(context); } // 別スレッド上で実行するデリゲート private void StartProc(object state) { int id = Thread.CurrentThread.ManagedThreadId; Debug.WriteLine(String.Format("StartProc ThreadId = {0}", id)); // UI 同期コンテキスト SynchronizationContext context = state as SynchronizationContext; if (context != null) { for (int i = 0; i < 10; ++i) { // これは処理時間稼ぎのため Thread.SpinWait(10000000); // UI 同期コンテキストからデリゲートを呼び出すので、UI スレッド上で実行される context.Post(AddTextToTextBox, i.ToString()); } } } // UI スレッド上で実行するデリゲート private void AddTextToTextBox(object state) { int id = Thread.CurrentThread.ManagedThreadId; Debug.WriteLine(String.Format("AddTextToTextBox ThreadId = {0}", id)); string text = state as string; textBox.Text += text + "\r\n"; }
実行結果:(出力ウインドウに出力)
button1_Click ThreadId = 10 StartProc ThreadId = 3 AddTextToTextBox ThreadId = 10 AddTextToTextBox ThreadId = 10 .... AddTextToTextBox ThreadId = 10 スレッド '* 別スレッド *' (0x390) はコード 0 (0x0) で終了しました。 AddTextToTextBox ThreadId = 10
出力ウインドウに出力されたものを見ると、button1_Click と AddTextToTextBox との ThreadId が同じになっていることが分かります。つまり、これらは同じスレッド上(この場合は UI スレッド)で実行されたことを意味しています。
ここでは SynchronizationContext クラスの役割を理解することが目的ですから、別スレッドで例外が発生した場合の対処法などのテーマは別の項で説明します。
さて、用語の説明の「スレッドプール」の項で説明しましたが、スレッドは一つの SynchronizationContext オブジェクト(以後、簡単のため、同期コンテキストと称します)を作成し、保持します。これはデフォルトの同期コンテキストとなります。コンソールアプリケーションや Windows サービスアプリケーションの場合は ThreadPool スレッドだけを持ちますが、Windows フォームアプリケーションおよび WPF アプリケーションは ThreadPool スレッドと UI スレッドとの 2 つのスレッドが与えられ、UI スレッドも一つの同期コンテキストを持ちます。ちなみに、同期コンテキストの実装クラスは以下のとおりです。
Windows フォームアプリケーション | System.Windows.Forms.WindowsFormsSynchronizationContext クラス |
WPF アプリケーション | System.Windows.Threading.DispatcherSynchronizationContext クラス |
−以上−