キャンセルが呼び出された後もスレッドが実行され続けるのはなぜですか?

シャハールショクラニ

この簡単なサンプルコードを考えてみましょう

var cts = new CancellationTokenSource();
var items = Enumerable.Range(1, 20);

var results = items.AsParallel().WithCancellation(cts.Token).Select(i =>
{
    double result = Math.Log10(i);                
    return result;
});

try
{
    foreach (var result in results)
    {
        if (result > 1)
            cts.Cancel();
        Console.WriteLine($"result = {result}");
    }
}
catch (OperationCanceledException e)
{
    if (cts.IsCancellationRequested)
        Console.WriteLine($"Canceled");
}

並列結果の結果のそれぞれについて、結果を次のように出力します。 result > 1

このコード出力は次のようなものです。

result = 0.9030899869919435
result = 0.8450980400142568
result = 0.7781512503836436
result = 0
result = 0.6020599913279624
result = 0.47712125471966244
result = 0.3010299956639812
result = 0.6989700043360189
result = 0.9542425094393249
result = 1
result = 1.0413926851582251 <-- This is normal
result = 1.2041199826559248 <-- Why it prints this value (and below)
result = 1.0791812460476249
result = 1.2304489213782739
result = 1.1139433523068367
result = 1.255272505103306
result = 1.146128035678238
result = 1.2787536009528289
result = 1.1760912590556813
result = 1.3010299956639813
Canceled

私の質問は、なぜ1を超える値を出力し続けるのですか?Cancel()トークンがプロセスを終了することを期待していました


アップデート1

@ mike-sの回答が提案されました:

ループ内(ループを中止する手段として)または長い操作の前にキャンセルトークンをチェックすることも役立ちます。

小切手を追加してみました

foreach (var result in results)
{
    if (result > 1)
        cts.Cancel();

    if (!cts.IsCancellationRequested) //<----Check the cancellation token before printing
        Console.WriteLine($"result = {result}");
}

それでも同じ結果の出力が得られます。

エリック・リペット

私の質問は、なぜ1を超える値を出力し続けるのですか?

あなたが100の空港から100の飛行機を飛ばすために100人のパイロットを雇ったと想像してください。それらの束が離陸し、「すべてのフライトをキャンセルします」というメッセージを送信します。さて、あなたがそのメッセージを送るとき、離陸速度で滑走路にたくさんの飛行機があります、そして、メッセージはそれらが空中にある後に到着しますこれらのフライトはキャンセルされません!

マルチスレッドプログラミングについて知っておくべき最も重要なことを発見しています。起こっていることのすべての可能な順序が発生する可能性があるかのように推論する必要がありますこれには、思ったよりも遅れて届くメッセージも含まれます。

特に、あなたの問題は、長い作業を並列化するように設計された並列化メカニズムの乱用の結果です停止するメッセージを送信するよりも実行に時間がかからない一連のタスクを作成しました。その場合、一部のタスクが停止するように指示された後に完了することは驚くべきことではありません。

トークンでCancel()を呼び出すと、プロセスが終了することを期待していました。

あなたの期待は完全に、完全に間違っています。その期待は決して現実に一致しないので、それを期待するのをやめてください。キャンセルトークンは、都合のよいときに操作をキャンセルする要求です。スレッドやプロセスを終了するのではありません。

ただし、場合でもなかったのスレッドを終了し、あなたはまだ、この動作を観察します。スレッドの終了は他のイベントと同様のイベントであり、そのイベントは瞬時ではありません。実行には時間がかかり、他のスレッドはそのスレッドの終了が実行されている間も作業を続行できます。

「都合の良いときに操作をキャンセルするリクエスト」の「便利」とはどういう意味ですか?

一歩後退しましょう。

実行する作業が非常に短い場合は、それをタスクとして表す必要はありません。ただ仕事をしてください!一般に、作業にかかる時間が約30ミリ秒未満の場合は、作業を行ってください。

したがって、すべてのタスクに長い時間がかかると仮定しましょう

では、なぜタスクに時間がかかるのでしょうか。一般的に2つの理由があります:

  • 別のシステムがいくつかのタスクを完了するのを待っています。ネットワークパケットやディスクの読み取りなどを待っています。

  • 大量の計算があり、CPUが飽和状態になっています。

最初の状況にあると仮定します。並列化は役に立ちますか?いいえ。郵便で荷物を待っている場合、1人、2人、10人、または100人を雇っても、荷物が早く届くわけではありません

しかし、それ2番目のケースに役立ちます。マシンに追加のCPUがある場合、約半分の時間で問題を解決するために2つのCPUを割り当てることができます。

したがって、タスクを並列化する場合、それはCPUが多くの作業を行っているためであると推測できます。

すごい。さて、「CPUは多くの仕事をする」という性質は何ですか?ほとんどの場合、どこかにループがあります

では、どうすればタスクをキャンセルできますか?スレッドを終了してタスクをキャンセルすることはありませんタスクにキャンセル依頼します。適切に設計されたタスクはキャンセルトークンを受け取り、そのループで、キャンセルトークンがタスクがキャンセルされたことを示しているかどうかを確認します。キャンセルは協力的です。タスクは協力してキャンセルされたかどうかを確認するタイミング決定する必要があります

キャンセルされたかどうか確認するの作業であり、実際のタスクから時間かかる作業であることに注意してくださいキャンセルされたかどうかを確認するために半分の時間を費やすと、タスクにかかる時間は2倍になります。そして、覚えて、タスクを並列化の点である、それは半分の長取る作るので、倍増それがタスクを実行するのにかかる時間は、非スターターです。

したがって、ほとんどのタスクは、キャンセルされたかどうかをループ全体で毎回チェックするわけではありません適切に設計されたタスクは、数ナノ秒ごとではなく、数ミリ秒ごとにチェックします。

それが「キャンセルとは都合の良いときにやめたいというリクエスト」という意味です。タスクは、正しく記述されていれば、応答性とパフォーマンスのバランスをとるために、キャンセルをチェックするのに適した時期を知っている必要があります。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

コルーチンが最初に呼び出し元のスレッドで実行されるのに、最初の一時停止ポイントの後、「Dispatchers.Unconfined」のDefaultExecutorで実行されるのはなぜですか?

分類Dev

TImage.Picture.LoadFromFileを呼び出した後、コードが続行されるのはなぜですか?

分類Dev

present(_:animated:completion :)メソッドを1回呼び出した後、ViewControllerが2回続けてモーダルに表示されるのはなぜですか?

分類Dev

デバウンスされた関数が呼び出された後、実行される前にキャンセルする方法は?

分類Dev

ウィジェットが表示されていなくても、Stateのbuild()メソッドが呼び出され続けるのはなぜですか?

分類Dev

スレッドが呼び出されたときにJFrameが表示されないのはなぜですか?

分類Dev

PHP-インクルードファイルが呼び出されたときに関数が実行されるのはなぜですか?

分類Dev

再帰呼び出しが実行された後、なぜこのステートメントが実行されるのですか?

分類Dev

このメソッドが呼び出されずに実行されるのはなぜですか?

分類Dev

デストラクタが呼び出された場合でも、C ++ 11でデタッチされたスレッドを実行できるのはなぜですか

分類Dev

ルートがエクスプレスで実行された後、「next」を呼び出せないのはなぜですか?

分類Dev

subscribeOn()が別のスレッドで呼び出されても、監視可能なスレッドがメインスレッドで実行される

分類Dev

通知がキャンセルされたときにdeleteIntent(PendingIntent)が呼び出されないのはなぜですか?

分類Dev

メソッドが呼び出されるたびにCompile()が実行されないのはなぜですか?

分類Dev

クロージャでは、メイン変数が呼び出されるたびにリセットされないのはなぜですか?

分類Dev

アクションが呼び出された後、エフェクトがサーバー時間実行されるのはなぜですか?

分類Dev

誰も呼び出さないのにpaintComponent()が実行されるのはなぜですか?

分類Dev

ドキュメントの削除後にdocChangeが2回呼び出されるのはなぜですか?

分類Dev

.ConfigureAwait(false)がブロックコードの下でずっと使用されている場合、ブロックされた非同期呼び出しの後のコードは同じコンテキストで続行されますか?

分類Dev

deinitメソッドが呼び出された後にオブジェクトにアクセスできるのはなぜですか?

分類Dev

unbindが呼び出された後にAndroidサービスがリセットされるのはなぜですか?

分類Dev

start()を呼び出した後、スレッドのコンストラクターがrun()メソッドに転送されるコントロールではなくmain()に戻るのはなぜですか?

分類Dev

非同期で実行中のメソッドが再度呼び出され、以前の呼び出し結果が廃止された場合に、メソッドを適切にキャンセルする方法

分類Dev

セレクターとして使用されているクラスまたはIDがHTMLから削除された後も、jQuery関数が呼び出されるのはなぜですか?

分類Dev

コントローラーが呼び出された後にhtml.erbファイルが呼び出されるのはなぜですか?

分類Dev

DB :: selectを呼び出すと、「接続がリセットされました」というメッセージが表示されるのはなぜですか?

分類Dev

DB :: selectを呼び出すと、「接続がリセットされました」というメッセージが表示されるのはなぜですか?

分類Dev

このスレッドプールがデッドロックしたり、何度も実行されたりするのはなぜですか?

分類Dev

Javaのinvokevirtualが呼び出されたメソッドのコンパイル時クラスを解決する必要があるのはなぜですか?

Related 関連記事

  1. 1

    コルーチンが最初に呼び出し元のスレッドで実行されるのに、最初の一時停止ポイントの後、「Dispatchers.Unconfined」のDefaultExecutorで実行されるのはなぜですか?

  2. 2

    TImage.Picture.LoadFromFileを呼び出した後、コードが続行されるのはなぜですか?

  3. 3

    present(_:animated:completion :)メソッドを1回呼び出した後、ViewControllerが2回続けてモーダルに表示されるのはなぜですか?

  4. 4

    デバウンスされた関数が呼び出された後、実行される前にキャンセルする方法は?

  5. 5

    ウィジェットが表示されていなくても、Stateのbuild()メソッドが呼び出され続けるのはなぜですか?

  6. 6

    スレッドが呼び出されたときにJFrameが表示されないのはなぜですか?

  7. 7

    PHP-インクルードファイルが呼び出されたときに関数が実行されるのはなぜですか?

  8. 8

    再帰呼び出しが実行された後、なぜこのステートメントが実行されるのですか?

  9. 9

    このメソッドが呼び出されずに実行されるのはなぜですか?

  10. 10

    デストラクタが呼び出された場合でも、C ++ 11でデタッチされたスレッドを実行できるのはなぜですか

  11. 11

    ルートがエクスプレスで実行された後、「next」を呼び出せないのはなぜですか?

  12. 12

    subscribeOn()が別のスレッドで呼び出されても、監視可能なスレッドがメインスレッドで実行される

  13. 13

    通知がキャンセルされたときにdeleteIntent(PendingIntent)が呼び出されないのはなぜですか?

  14. 14

    メソッドが呼び出されるたびにCompile()が実行されないのはなぜですか?

  15. 15

    クロージャでは、メイン変数が呼び出されるたびにリセットされないのはなぜですか?

  16. 16

    アクションが呼び出された後、エフェクトがサーバー時間実行されるのはなぜですか?

  17. 17

    誰も呼び出さないのにpaintComponent()が実行されるのはなぜですか?

  18. 18

    ドキュメントの削除後にdocChangeが2回呼び出されるのはなぜですか?

  19. 19

    .ConfigureAwait(false)がブロックコードの下でずっと使用されている場合、ブロックされた非同期呼び出しの後のコードは同じコンテキストで続行されますか?

  20. 20

    deinitメソッドが呼び出された後にオブジェクトにアクセスできるのはなぜですか?

  21. 21

    unbindが呼び出された後にAndroidサービスがリセットされるのはなぜですか?

  22. 22

    start()を呼び出した後、スレッドのコンストラクターがrun()メソッドに転送されるコントロールではなくmain()に戻るのはなぜですか?

  23. 23

    非同期で実行中のメソッドが再度呼び出され、以前の呼び出し結果が廃止された場合に、メソッドを適切にキャンセルする方法

  24. 24

    セレクターとして使用されているクラスまたはIDがHTMLから削除された後も、jQuery関数が呼び出されるのはなぜですか?

  25. 25

    コントローラーが呼び出された後にhtml.erbファイルが呼び出されるのはなぜですか?

  26. 26

    DB :: selectを呼び出すと、「接続がリセットされました」というメッセージが表示されるのはなぜですか?

  27. 27

    DB :: selectを呼び出すと、「接続がリセットされました」というメッセージが表示されるのはなぜですか?

  28. 28

    このスレッドプールがデッドロックしたり、何度も実行されたりするのはなぜですか?

  29. 29

    Javaのinvokevirtualが呼び出されたメソッドのコンパイル時クラスを解決する必要があるのはなぜですか?

ホットタグ

アーカイブ