別スレッドを使って画像処理を行い、処理後のビットマップをUIのImageコントロールに反映する処理を作成した。
UIスレッドとは別のスレッドからImage.Sourceを更新するには、Dispatcher.BeginInvokeあるいはDispatcher.Invokeを使用すればいいらしいのでやってみた!
ダメな例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Task.Run(() => //別スレッドでimage_show()を走らせる { image_show(); }); private void image_show(){ var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb48, null); image_processing();//bitmapに対してのなにかしらの処理 image.Dispatcher.BeginInvoke(new Action(() => { image.Source=bitmap;//imageコントロールにbitmapをアタッチ })); } |
まずはこんな感じでimageコントロールにbitmapをアタッチしている部分を単純にBeginInvokeで囲ってみた。
そうすると、例外は発生しないものの画像が全く表示されない。
原因と対策
この原因はおそらく、imageコントロールの描画処理はUIスレッドで動くのに対し、そこに渡したいbitmapは別スレッドにあるものだからだと思う。
ググってみると、Freezeメソッドを使用することで別スレッドからbitmapを使えるようになるらしい。
Freezable Objects Overview / Microsoft Docs
ここを見ると、
Thread safety: a frozen Freezable can be shared across threads.
と書いてある。Freezeされたオブジェクトはスレッド間で共有できるということ。
だからbitmapをFreeze()してやるとUIスレッドへ共有できるわけね。
bitmapをFreezeしてみる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Task.Run(() => //別スレッドでimage_show()を走らせる { image_show(); }); private void image_show(){ var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb48, null); image_processing();//bitmapに対してのなにかしらの処理 bitmap.Freeze();//bitmapをFreezeする image.Dispatcher.BeginInvoke(new Action(() => { image.Source=bitmap;//imageコントロールにbitmapをアタッチ })); } |
こうすることで、問題なくimageコントロールへbitmapを表示することができた。
非同期の処理にするとスレッド間のやりとりとか、いろいろ考える事が増えて大変ですなぁー