CancellationToken.ThrowIfCancellationRequested の例外(非同期処理内の例外)を補足できないと勘違いして大分ハマったので記事にしておく。
ThrowIfCancellationRequested の例外を補足できない
VB.NET での最近の非同期処理について調べながら(実際はほとんど C# を調べることになるのだが)、ネットで見つけたサンプルコードを参考にしつつゴニョゴニョとプログラムを書いて試していた。
が、どうしても非同期処理内でのキャンセル処理がうまくいかない。
(IsCancellationRequested の使用については問題ないのだが)CancellationToken.ThrowIfCancellationRequested が実行されるところで下記のようなエラーが出てコードが止まってしまう。
ユーザーが処理していない例外
System.OperationCanceledException: ‘操作は取り消されました。’
いやいや、もちろん、このコードを書いた関数を呼び出してるほうの関数内で Try ~ Catch してますよ。でもその Try ~ Catch が例外を補足してくれず、上記の場所で止まってしまうわけ。
IsCancellationRequested だけを使って非同期処理のキャンセルに対応することも可能だが、ThrowIfCancellationRequested の方が好ましい場面もありそうだし、止まってしまう理由が分からないままなのも気持ちが悪い。できればこの問題を解決しておきたいと思った。
非同期処理というある意味特殊なことをやってるので、それ特有のややこしい事情のためにこういうことが起きるのかな?と思って、そのことが書かれている記事を探し回りました。
でも、ThrowIfCancellationRequested の(C# の)サンプルプログラムの記事はたくさん見つかるけど、例外を捕捉できない件に触れてる記事が見つからない。みんなそのコードでホントにちゃんと動いてるの? 私だけ動いてないの?(?_?) と悩みました。
理由と対処法
しかしついにそのことについて触れてる記事を見つけました。(ThrowIfCancellationRequested というキーワードが使われてない記事だからなかなか見つからなかった)その記事がこちら。
詳細は上記の記事を参照してくださいね。
要は、実は ThrowIfCancellationRequested の例外はちゃんと捕捉できてたんだよぉ~ん、という話だ。コードに問題はなかったのだ。
そして、ThrowIfCancellationRequested の例外の問題というよりも、非同期処理内の例外の問題ということも分かった。
コードがエラーを出して止まってしまったのは、例外発生場所を含む非同期処理のコードをデバッグモードでテストしてたから、ということらしい。デバッグモードでは、例外発生時に非同期処理を抜ける前、確認のため(親切心で)いったん止まってくれてる・・・ということらしい。
対処法1(VS の設定を変えない方法)
コードが止まったら、そこで再び F5 を押してみれば処理が継続されて意図通りに動作することが分かる(本当に捕捉できてない例外だったら継続できない)。
最初から Ctrl + F5(デバッグなし)で実行するか、Release モードで実行しても、きちんとエラーを補足して意図通りに動作することが分かる。
対処法2(VS の設定を変える方法)
VS の設定を変えると、デバッグモードでもいちいち止まらないようにできる。たとえば、Microsoft の「例外処理 (タスク並列ライブラリ)」の記事の中に下記の記述がある。
注意
[マイ コードのみ] が有効になっている場合、Visual Studio では、例外をスローする行で処理が中断され、”ユーザー コードで処理されない例外” に関するエラー メッセージが表示されることがあります。 このエラーは問題にはなりません。 F5 キーを押して続行し、以下の例に示す例外処理動作を確認できます。 Visual Studio による処理が最初のエラーで中断しないようにするには、 [ツール] メニューの [オプション]、[デバッグ] の順にクリックし、[全般] で [マイ コードのみを有効にする] チェック ボックスをオフにします。
[マイ コードのみを有効にする]は規定でオンになっていると思う。オンとオフの状態を試してみたら下記のようになった。
- デバッグモードで[マイ コードのみを有効にする]がオンの場合、
非同期処理内の ThrowIfCancellationRequested の例外は補足できない(ように見えて止まる)が、非同期処理とは無関係な例外は捕捉できる。 - デバッグモードで[マイ コードのみを有効にする]がオフの場合、
非同期処理内の ThrowIfCancellationRequested の例外を補足できる(いちいち止まらない)し、非同期処理とは無関係な例外も捕捉できる。 - デバッグモードで[マイ コードのみを有効にする]がオンでもオフでも、Try ~ Catch してない例外が発生すればコードはそこで止まる。
または、エラーが出た下図のところで[例外設定]をクリックして、指定した例外のところで止まらないように設定を変更することもできる。
ただ、ほんとに止まるべきところで止まってくれないということも起こり得ると思うので、VS の設定を変えて対応するときは十分注意したほうが良いだろう。