mcpField
T
thread-abort-migration

thread-abort-migration

github.com/dotnet開発5.8k3.4k 回インストール1 日前に更新
リポジトリ

thread-abort-migration について

thread-abort-migrationは、スレッド終了APIに依存する.NET Frameworkコードを、最新の.NETの協調キャンセル方式へ移行する開発者を支援します。利用パターンを分類して代替策を選び、ビルド、参照、テストで移行を確認します。

主な機能

  1. 01スレッド終了APIの洗い出し
  2. 02キャンセルパターン別の利用分類
  3. 03中断処理を協調キャンセルに置換
  4. 04ASP.NETのレスポンス終了処理
  5. 05ビルド、参照、テストの検証
  6. 06クリーンアップとタイムアウト動作の維持

活用シーン

  1. 01.NET Frameworkプロジェクトを.NET 6+へ移行
  2. 02キャンセル可能な処理のThread.Abortを置換
  3. 03中断ベースのタイムアウト処理を移行
  4. 04SYSLIB0006などの移行課題を解消

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dotnet/skills --skill thread-abort-migration

インストール後、Claude Code で/thread-abort-migrationを入力して読み込まれたか確認します。

詳細

Thread.Abort の移行

このスキルは、Thread.Abort を使用する .NET Framework のコードを、最新の .NET(6 以降)で必要となる協調的キャンセルモデルへ移行するのに役立ちます。最新の .NET では Thread.Abort を呼び出すと PlatformNotSupportedException がスローされます。マネージドスレッドを強制終了する方法はありません。このスキルでは、まず使用パターンを特定し、適切な置き換え方法を適用します。

使用する場面

  • Thread.Abort を呼び出す .NET Framework プロジェクトを .NET 6 以降へ移行する場合
  • 制御フローやクリーンアップ処理に使用されている ThreadAbortException の catch ブロックを置き換える場合
  • 保留中の中断を取り消す Thread.ResetAbort の呼び出しを削除する場合
  • ブロック中のスレッドを起こすために使用している Thread.Interrupt を置き換える場合
  • 内部で Thread.Abort を呼び出す Response.End または Response.Redirect(url, true) を使用する ASP.NET コードを移行する場合
  • ターゲットフレームワークの変更後に発生した PlatformNotSupportedException または SYSLIB0006 の警告を解消する場合

使用しない場面

  • コードで Thread.Join、Thread.Sleep、または Thread.Start のみを使用しており、中断処理も ThreadAbortException の catch ブロックもない場合。 これらの API は最新の .NET でも同じように動作するため、移行は不要です。ここで作業を止め、移行が不要であることをユーザーに伝えてください。最新化(例:Task.Run、Parallel.ForEach)を提案する場合は、これらが Thread.Abort の移行とは無関係な任意の改善であり、既存のコードはターゲットフレームワーク上でそのまま正しくコンパイルおよび実行できることを必ず明記してください。
  • プロジェクトを今後も .NET Framework 上で使い続ける場合
  • Thread.Abort の使用箇所が、自分で制御できないサードパーティライブラリ内にある場合

入力

入力必須説明
ソースプロジェクトまたはソリューションはいThread.Abort の使用箇所を含む .NET Framework プロジェクト
ターゲットフレームワークはいターゲットとする最新の .NET バージョン(例:net8.0)
Thread.Abort の使用箇所推奨Thread.Abort、ThreadAbortException、Thread.ResetAbort、または Thread.Interrupt を参照しているファイルやクラス

ワークフロー

コミット方針: 置き換えパターンごとにコミットし、移行内容をレビューおよび二分探索できるようにします。関連する呼び出し箇所(例:キャンセル可能なすべての作業ループ)は、1 つのコミットにまとめます。

ステップ 1:すべてのスレッド終了処理を洗い出す

コードベースを検索し、スレッド終了関連の API をすべて特定します。

  • Thread.Abort および thread.Abort()(インスタンス呼び出し)
  • catch ブロック内の ThreadAbortException
  • Thread.ResetAbort
  • Thread.Interrupt
  • Response.End()(ASP.NET Framework で内部的に Thread.Abort を呼び出します)
  • Response.Redirect(url, true)(true パラメーターによって Thread.Abort が呼び出されます)
  • SYSLIB0006 のプラグマによる警告抑制

各使用箇所を記録し、中断の意図を分類します。

ステップ 2:各使用パターンを分類する

各使用箇所を、次のパターンのいずれかに分類します。

パターン説明現代的な代替手段
キャンセル可能な作業ループ要求に応じて停止する必要があるループを実行するスレッドループ内で CancellationToken を確認する
タイムアウトの適用制限時間を超過したスレッドを中断するCancellationTokenSource.CancelAfter または遅延を伴う Task.WhenAny
ブロッキング呼び出しの中断起動する必要がある Sleep、WaitOne、または Join でブロックされたスレッドCancellationToken.WaitHandle を使った WaitHandle.WaitAny、または非同期の代替手段
ASP.NET リクエストの終了Response.End または Response.Redirect(url, true)アクションメソッドから戻る。HttpContext.RequestAborted を使用する
制御フローとしての ThreadAbortExceptionThreadAbortException を調べてクリーンアップ処理を決定する catch ブロック代わりに OperationCanceledException を捕捉し、明示的にクリーンアップする
実行継続のための Thread.ResetAbortスレッドを存続させるため、中断を捕捉して ResetAbort を呼び出すCancellationToken.IsCancellationRequested を確認し、継続するかどうかを判断する
協調しないコードの終了キャンセル確認を行うよう変更できないコードを実行するスレッドを終了させる作業を別プロセスに移し、Process.Kill を使用する

重要: 根本的なパラダイムシフトは、プリエンプティブなキャンセル(ランタイムが例外を強制的に挿入する)から協調的なキャンセル(コードが自発的にキャンセル要求を確認して応答する必要がある)への移行です。すべての呼び出し箇所について、対象のコードを協調動作するよう変更できるか評価する必要があります。

ステップ 3:各パターンに代替手段を適用する
  • キャンセル可能な作業ループ:CancellationToken パラメーターを追加します。ループ条件を置き換えるか、安全なチェックポイントで token.ThrowIfCancellationRequested() を追加します。呼び出し元で CancellationTokenSource を作成し、Thread.Abort() の代わりに Cancel() を呼び出します。
  • タイムアウトの適用:new CancellationTokenSource(TimeSpan.FromSeconds(n)) または cts.CancelAfter(timeout) を使用します。トークンを作業に渡します。タスクベースのコードでは、Task.WhenAny(workTask, Task.Delay(timeout, cts.Token)) を使い、遅延が先に完了した場合はソースをキャンセルします。キャンセルすると、遅延の内部タイマーも破棄されます。
  • ブロッキング呼び出しの中断:Thread.Sleep(ms) を Task.Delay(ms, token) または token.WaitHandle.WaitOne(ms) に置き換えます。ManualResetEvent.WaitOne() を WaitHandle.WaitAny(new[] { event, token.WaitHandle }) に置き換えます。
  • ASP.NET リクエストの終了:Response.End() は完全に削除し、メソッドから戻るだけにします。Response.Redirect(url, true) は Response.Redirect(url)(true の endResponse パラメーターなし)に置き換えるか、リダイレクト結果を返します。ASP.NET Core では、長時間実行されるリクエスト処理のキャンセルトークンとして HttpContext.RequestAborted を使用します。
  • 制御フローとしての ThreadAbortException:catch (ThreadAbortException) を catch (OperationCanceledException) に置き換えます。クリーンアップ処理は finally ブロックまたは CancellationToken.Register コールバックに移します。OperationCanceledException を捕捉して握りつぶしてはいけません。具体的な回復処理がない限り、例外を伝播させます。
  • 実行継続のための Thread.ResetAbort:処理ループでのキャンセル後に次の作業単位へ進めるよう、「中断可能な」作業単位を分割します。スレッドの破棄を防ぐために ResetAbort に頼らないでください。各作業単位の後に token.IsCancellationRequested を確認し、継続するかどうかを判断します。既存のソースをリセットしようとするのではなく、新しい作業単位ごとに新しい CancellationTokenSource(必要に応じて親トークンにリンク)を作成します。
  • 協調しないコードの終了:コードを変更して CancellationToken を受け取るようにできない場合(サードパーティーライブラリやネイティブ呼び出しなど)、作業を子プロセスに移します。ホストプロセスは stdin/stdout または IPC を介して通信し、タイムアウトした場合は Process.Kill を呼び出します。
ステップ 4: 削除された API のクリーンアップ

すべてのパターンを移行した後、残っている参照を削除または置き換えます。

削除された API置き換え
Thread.Abort()CancellationTokenSource.Cancel()
ThreadAbortException の catch ブロックOperationCanceledException の catch ブロック
Thread.ResetAbort()token.IsCancellationRequested を確認し、続行するかどうかを判断します
Thread.Interrupt()CancellationToken を介してシグナルを送るか、ManualResetEventSlim を設定します (こちらも廃止予定: .NET 9 の SYSLIB0046)
Response.End()呼び出しを削除し、メソッドから return します
Response.Redirect(url, true)endResponse なしの Response.Redirect(url)、またはリダイレクト結果を返します
#pragma warning disable SYSLIB0006Thread.Abort 呼び出しを置き換えた後に削除します
ステップ 5: 移行の検証
  1. 新しいフレームワークをターゲットにしてプロジェクトをビルドします。SYSLIB0006 警告が 0 件であり、Thread.Abort に関連するコンパイルエラーがないことを確認します。
  2. コードベース内に Thread.Abort、ThreadAbortException、Thread.ResetAbort、または Thread.Interrupt への参照が残っていないか検索します。
  3. 既存のテストを実行します。テストがクリーンアップやタイムアウトのために Thread.Abort に依存していた場合は、CancellationToken を使用するように更新します。
  4. タイムアウトのシナリオでは、キャンセルが要求された後に処理が妥当な時間内に実際に停止することを確認します。
  5. ブロッキング呼び出しのシナリオでは、トークンがキャンセルされたときにブロックされたスレッドが速やかに起床することを確認します。

検証

  • 移行後のコードに Thread.Abort への参照が残っていない
  • ThreadAbortException の catch ブロックが残っていない
  • Thread.ResetAbort の呼び出しが残っていない
  • SYSLIB0006 の pragma 抑制が残っていない
  • プロジェクトがターゲットフレームワークに対してスレッドアボート関連の警告なしでクリーンにビルドされる
  • キャンセル可能なすべての処理が CancellationToken パラメーターを受け取る
  • タイムアウトのシナリオで CancellationTokenSource.CancelAfter または同等のものを使用している
  • ブロッキング呼び出しで token.WaitHandle を伴う WaitHandle.WaitAny または非同期の代替手段を使用している
  • 既存のテストがパスするか、協調的キャンセル用に更新されている

よくある落とし穴

落とし穴解決策
CancellationToken パラメーターを追加したものの、長時間実行されるコードで確認していないループ内や負荷の高い処理の間に、定期的なチェックポイントとして token.ThrowIfCancellationRequested() を挿入します。キャンセルが機能するのは、コードがそれに協調する場合のみです。
呼び出しチェーン全体にトークンを渡していないチェーン内のすべての非同期メソッドまたは長時間実行されるメソッドで、CancellationToken を受け取り、次のメソッドに渡す必要があります。チェーン内のメソッドがひとつでもトークンを無視すると、その時点でキャンセルが滞ります。
CancellationToken によって、Thread.Sleep や socket.Receive などのブロッキングする同期呼び出しが中断されると期待しているこれらの呼び出しはトークンを確認しません。Thread.Sleep(ms) を token.WaitHandle.WaitOne(ms) に置き換えます。同期 I/O は、CancellationToken を受け取る非同期オーバーロードに置き換えます。
OperationCanceledException をキャッチして握りつぶしているOperationCanceledException は呼び出し元まで伝播させます。キャンセル後の処理(ログ記録、クリーンアップ、結果の返却)を決定する最上位のオーケストレーション箇所でのみキャッチします。
CancellationTokenSource を破棄していないCancellationTokenSource は IDisposable です。using ステートメントで囲むか、finally ブロックで破棄します。破棄しないと、タイマーやコールバックがリークします。
キャンセルは即座に行われると思い込んでいる協調的キャンセルは、次のチェックポイントでのみ有効になります。作業項目が大きい場合や、チェック間隔が長い場合は、キャンセルが遅れることがあります。許容できる遅延に基づいて、チェックポイントの頻度を設計してください。
Thread.Abort の代わりに Thread.Interrupt を使用しているThread.Interrupt も、現代の .NET では推奨されません。WaitSleepJoin 状態のスレッドでのみ機能し、別の例外型である ThreadInterruptedException をスローします。CancellationToken による通知に置き換えてください。
クリーンアップ処理を移行せずに ThreadAbortException の catch ブロックを削除しているThreadAbortException の catch ブロックには、重要なクリーンアップ(ロックの解放、トランザクションのロールバック)が含まれていることがよくあります。catch を削除する前に、この処理を finally ブロックまたは CancellationToken.Register コールバックに移してください。

詳細情報

コメント