2

TPLを使用して、実行時間の長いタスクをスレッドプールにキューに入れています。一部のタスクはしばらくの間ブロックされる可能性があるため、次のパターンを使用してそれらをキャンセルしています。

private void RunAction(Action action, CancellationTokenSourceWithException cts)
{

    try
    {
        s_logger.Info("Starting action on thread ID: {0}", Utils.GetCurrentNativeThreadId());

        Thread taskThread = Thread.CurrentThread;
        cts.Token.Register(() => InterruptTask(taskThread));

        s_logger.Info("Running next action");
        action();
    }
    catch (Exception e)
    {
        cts.Cancel(e);
        throw;
    }

このように呼び出すcts.Cancel()と、タスクスレッドがブロックされている場合に、タスクスレッドが中断されます。ただし、これにより問題が発生しました。スレッドが実際にThreadInterruptedExceptionを取得したかどうかはわかりません。それを呼び出すことは可能ですThread.Interrupt()が、スレッドは完了するまで実行され、タスクは単に終了します。その場合、スレッドプールスレッドにはThreadInterruptedExceptionの形式のカチカチ音をたてる爆弾があり、別のタスクがこのスレッドで実行されてブロックしようとすると、この例外が発生します。

Thread.ResetInterrupted()ここでは(と同様の)方法がThread.ResetAbort()役立ちますが、存在しないようです。次のようなものを使用できます。

try
{
    someEvent.Wait(10);
}
catch (ThreadInterruptedException) {}

ThreadInterruptedExceptionを飲み込みますが、見た目は醜いです。

誰かが代替案を提案できますか?スレッドプールスレッドでThread.Interruptを呼び出すのは間違っていますか?タスクをキャンセルする最も簡単な方法のようです。イベントなどを使用した協調キャンセルは、使用するのがはるかに面倒であり、タスクから使用するすべてのクラスに伝播する必要があります。

4

2 に答える 2

5

独自のコードを実行していないときにスレッド プールのスレッドがいつブロックされるかがわからないため、これを行うことはできません。

あなたが言及した問題とは別に、独自のコードを実行していないときにスレッドがブロックすることを決定した場合、それはThreadInterruptException処理されず、アプリはすぐに終了します。tryこれは、競合状態があるため、 /block/catchガードで回避できないものです:Thread.Interruptが呼び出されたときにガードが完了したばかりである可能性があるため、ランタイムがその時点でスレッドをブロックすることを決定した場合、クラッシュが発生します。

したがって、使用Thread.Interruptは実行可能なオプションではなく、確実に協調的なキャンセルを設定する必要があります。

それとは別に、そもそもこれらのタスクにスレッドプールを使用するべきではありません(ただし、十分なデータがありません.ドキュメントを引用しています(私の強調):

バックグラウンド処理を必要とする短いタスクがある場合、マネージ スレッド プールは、複数のスレッドを利用する簡単な方法です。

スレッド プール スレッドを使用する代わりに、独自のスレッドを作成して管理することが適切なシナリオがいくつかあります。

  • ...
  • スレッドが長時間ブロックされる原因となるタスクがあります。スレッド プールには最大数のスレッドがあるため、多数のスレッド プール スレッドがブロックされると、タスクの開始が妨げられる可能性があります。
  • ...

したがって、独自のスレッド プールの使用を検討することをお勧めします (非常に評判の良い実装がここにあります)。

于 2012-08-16T11:48:58.323 に答える
1

単純。呼び出されるアクションに CancellationToken を渡し、キャンセルが通知されたときにアクションを実行する必要があります。TPL スレッドをいじるInterruptのは間違いなく間違ったアクションであり、TPL を「混乱した」状態にします。ずっとキャンセルパターンを採用。

于 2012-08-16T12:08:07.763 に答える