2019年9月10日火曜日

【Winform】別スレッドからGUIを操作する

意味が分かっていても上手く動いてくれずにデッドロックするケースが多い問題である
「別スレッドからGUIの操作」のお話ですが、ようやく腑に落ちる対処方法が分かったのでメモ。

【問題の背景】

基本ですが、アプリは必ずメインスレッドが存在していてWinformだとスレッドID=1になる。
これがProgram.csの先頭から実行してGUIを表示したりする。

んで、画面上のボタンを押したら別スレッドが起動して重い処理を実行する。

その別スレッド内部でエラーが発生した場合、
例外を投げてtry~catch先でエラーハンドリングするなら特に問題なし。

別スレッド内部でエラーハンドリングを行ってしまおうってするとデッドロックしてしまうケースがある。

【ピンポイントな原因】

なぜデッドロックしてしまうかというと、メインスレッドが別スレッドの完了待ち状態でロックしているから。

これに尽きる。

言い換えれば、
メインスレッドは別スレッドの完了待ち状態で、
別スレッドはメインスレッドにGUI操作を要求したからデッドロックに陥っている。。

ここまで分かればひとまずOK。

~~余談~~
非同期に処理して、別スレッドから完了通知を受け取る方式なら回避できる。
この実装はいわゆるasync/awaitになるのだけど、理解するのに結構手間取るんだけどね。。

【デッドロック確認方法】
デッドロックした場合、デバッガ上で確認する事が出来る方法があって、
デバッガ上のスレッド一覧にあるメインスレッドがソース上のどの位置にいるかで判断が付く。

現在のスレッドが1以外の別スレッドで
メインスレッドの現在位置が
 Application.Run(new Form());

ならば問題なし!

もし、Wait(); とかそういう場所で止まっているなら必ずデッドロックが発生します。

【対処法】
そのデッドロックポイントを見つけたらInvokeRequireとかTaskのasync/awaitを用いて回避するのが一番簡単。

意外とこの仕組みを理解しておかないと色んなプログラムで同じようなハマりポイントでつまずくと思うので、ぜひとも押さえておきたいポイントです。

0 件のコメント:

Androider