【入門編】VB.NETにおけるCancellationTokenを用いた安全な非同期処理のキャンセルとタイムアウト制御 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

こんにちは、若きアーキテクトの皆さん。
いつもマクロの自動記録や、単純なボタンクリックの裏側に情熱を注いでいるあなたの姿を、私は頼もしく思っています。

「プログラムを実行したけれど、処理が重すぎてPCが固まってしまった」「途中で止めたくても、強制終了するしかない……」
そんな経験はありませんか?

Excel VBAの「Escキー連打」から卒業し、真のエンジニアとして一歩踏み出すために避けて通れないのが、.NETにおける「非同期処理の制御」です。今日は、VB.NETで重い処理をスマートに、そして安全に中断するための魔法の杖、「CancellationToken」について、その神髄を伝授しましょう。

ここをマスターすれば、あなたのプログラムは「止まらない暴走列車」から、乗客(ユーザー)の要望に完璧に応える「高級リムジン」へと進化しますよ。

1. なぜ「キャンセル」が必要なのか?

非同期処理(Async/Await)を使えば、重い計算やネットワーク通信中も画面が固まらなくなります。しかし、画面が動くからこそ、ユーザーはこう思うのです。

「あ、やっぱり今の検索、条件が間違ってたから止めたいな」
「応答が遅すぎるから、10秒経ったら自動で諦めてほしい」

この「やっぱりやめた」や「タイムアウト」を優雅にこなす仕組みが、.NETの標準作法である「キャンセル・トークン・パターン」です。

2. 登場人物は2人だけ:Source と Token

この仕組みを理解するために、駅伝をイメージしてください。

1. CancellationTokenSource (CTS)

  • 「監督」の役割です。
  • 「中止!」という命令を出す権限を持っています。

2. CancellationToken (CT)

  • 「タスキ(連絡事項)」の役割です。
  • 「中止命令が出ているかどうか」という情報を持ち、選手(非同期タスク)に渡されます。

監督(CTS)が中止の旗を振ると、その情報がタスキ(CT)を通じて選手に伝わり、選手はキリの良いところで走るのをやめる。これが基本構造です。

3. 実践:安全に止まる非同期処理の書き方

では、具体的なコードを見てみましょう。
ボタンをクリックすると重い処理が始まり、もう一度別のボタンを押すと安全に止まる仕組みです。

Imports System.Threading

Public Class FrmMain
‘ 監督(Source)を保持する変数。
‘ ライフサイクルを管理するため、クラスレベルで定義します。
Private _cts As CancellationTokenSource

”’

”’ 実行ボタンのクリックイベント
”’

Private Async Sub BtnStart_Click(sender As Object, e As EventArgs) Handles BtnStart.Click
‘ 1. 以前の命令が残っていたら破棄し、新しく監督(Source)を生成
_cts?.Dispose()
_cts = New CancellationTokenSource()

Try
BtnStart.Enabled = False
TxtStatus.Text = “処理を開始しました…”

‘ 2. 監督から「タスキ(Token)」を受け取って、非同期メソッドに渡す
Await DoHeavyWorkAsync(_cts.Token)

TxtStatus.Text = “すべての処理が正常に完了しました!”

Catch ex As OperationCanceledException
‘ 3. キャンセルされた時に投げられる「特別な例外」をキャッチする
TxtStatus.Text = “ユーザーによって処理が中断されました。”

Catch ex As Exception
‘ その他の予期せぬエラー
TxtStatus.Text = $”エラーが発生しました: {ex.Message}”

Finally
BtnStart.Enabled = True
End Try
End Sub

”’

”’ キャンセルボタンのクリックイベント
”’

Private Sub BtnCancel_Click(sender As Object, e As EventArgs) Handles BtnCancel.Click
‘ 4. 監督が「中止!」のサインを出す
_cts?.Cancel()
End Sub

”’

”’ 重い処理をシミュレートするメソッド
”’

”’ タスキ(キャンセル情報) Private Async Function DoHeavyWorkAsync(ct As CancellationToken) As Task
‘ 10回ほど重いループ処理を行う例
For i As Integer = 1 To 10
‘ 【重要】各ステップの開始時に、中止命令が出ていないか確認する
‘ 中止命令が出ていたら、ここで OperationCanceledException が投げられる
ct.ThrowIfCancellationRequested()

‘ 1秒待機(ここでもタスキを渡すことで、待機中にキャンセルが可能になる)
Await Task.Delay(1000, ct)

‘ 進捗表示など(メインスレッドへの報告)
Console.WriteLine($”{i}番目のステップが完了…”)
Next
End Function
End Class

4. ここがプロの視点:なぜ `ThrowIfCancellationRequested` を使うのか?

初心者のうちは、`If ct.IsCancellationRequested Then Return` と書きたくなるかもしれません。しかし、プロのコードでは `ct.ThrowIfCancellationRequested()` を推奨します。

理由は、「キャンセルは異常終了ではなく、合意の上での中断である」ということを、例外(OperationCanceledException)を通じて呼び出し元に明確に伝えるためです。これにより、呼び出し側は「正常に終わったのか」「エラーで落ちたのか」「キャンセルしたのか」を厳密に区別して処理できるようになります。

5. 応用:タイムアウトを自動化する

「3秒待っても反応がなければ自動でキャンセルしたい」という場合も、この仕組みなら一行です。

‘ 3000ミリ秒(3秒)後に自動でキャンセルサインを出す設定
_cts = New CancellationTokenSource(3000)

これだけで、ネットワークのタイムアウト処理などが驚くほど簡単に実装できます。監督が最初から「3秒経ったら旗を振るぞ」と決めている状態ですね。

6. 陥りやすいエラーと注意点

1. Disposeを忘れない
`CancellationTokenSource` は内部でタイマーなどのリソースを使用します。使い終わったら(あるいは次に新しく作る前に)必ず `Dispose()` を呼び出す癖をつけましょう。
2. ライブラリ関数にも Token を渡す
`Task.Delay` や `HttpClient.GetAsync` など、.NET Framework/.NET Coreの多くの非同期メソッドは、引数に `CancellationToken` を受け取れるようになっています。これらを呼び出す際は、必ず自分の持っているタスキを「横流し」してあげてください。そうすることで、深い階層の処理まで一斉に、安全に止めることができます。

結びに代えて

いかがでしたか?
「キャンセル」を制する者は、アプリケーションの「品質」を制します。
ユーザーが「いつでも止められる」という安心感を持って操作できるツールは、それだけで信頼に値する素晴らしいソフトウェアです。

最初は少し難しく感じるかもしれませんが、`Source` を作って `Token` を渡す、このリズムを指に覚え込ませてください。ここをクリアすれば、Visual Basic (VB.NET) の基本、そしてモダンな非同期プログラミングの基礎はバッチリですよ。

あなたのコードが、より洗練され、多くの人を助けるものになることを願っています。
また次の講義でお会いしましょう!

タイトルとURLをコピーしました