16

正直なところ、私は死んだ打ち負かそうとしているわけではありません。スレッドの強制終了に関するすべてのアドバイスを読みましたが、コードを検討してください。次のことを行います。

  1. スレッドを開始します(StartThreadメソッド経由)
  2. データベースを呼び出して、ServiceBroker キュー内の何かを探します。コマンドに注意してくださいWAITFOR。これは、キューに何かが入るまで待機することを意味します。このすべてのMonitorQueue方法で。
  3. スレッドを殺します。私は試し.Interruptました-それはまったく何もしないようです。.Abortそれから、決して使用してはならないを試しましたが、それでも何もしませんでした。

    Thread thxMonitor = new Thread(MonitorQueue);
    void StartThread() {
        thxMonitor.Start();
    }
    
    void MonitorQueue(object obj) {
        var conn = new SqlConnection(connString);
        conn.Open();
        var cmd = conn.CreateCommand();
        cmd.CommandTimeout = 0; // forever and ever
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = "WAITFOR (RECEIVE CONVERT(int, message_body) AS Message FROM SBQ)";
    
        var dataTable = new DataTable();
        var da = new SqlDataAdapter(command);
    
        da.Fill(dataTable);
        da.Dispose();
    }
    
    void KillThreadByAnyMeansNecessary() {
        thxMonitor.Interrupt();
        thxMonitor.Abort();
    }
    

実際にスレッドを強制終了することは可能ですか?

4

5 に答える 5

8

私はあなたの質問に答えないのは嫌いですが、これを別の方法で行うことを検討してください。T-SQLでは、TIMEOUTパラメータをWAITFORで指定できるため、メッセージが一定期間受信されない場合、ステートメントは終了し、再試行する必要があります。あなたはこれあなたが待たなければならないパターンで何度も何度も見ます。トレードオフは、要求されたときにスレッドがすぐに終了しないことです。スレッドが終了する前に、タイムアウトが期限切れになるのを待つ必要があります。

これを早く実行したいほど、タイムアウト間隔は短くなります。それを即座に実現したいですか?次に、代わりにポーリングする必要があります。

static bool _quit = false;

Thread thxMonitor = new Thread(MonitorQueue);
void StartThread() {
    thxMonitor.Start();
}

void MonitorQueue(object obj) {

    var conn = new SqlConnection(connString);
    conn.Open();
    var cmd = conn.CreateCommand();
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = "WAITFOR (RECEIVE CONVERT(int, message_body) AS Message FROM SBQ) TIMEOUT 500";

    var dataTable = new DataTable();    

    while(!quit && !dataTable.AsEnumerable().Any()) {
        using (var da = new SqlDataAdapter(command)) {    
            da.Fill(dataTable);
        }
    }
}

void KillThreadByAnyMeansNecessary() {
    _quit = true;
}

編集

これはキューをポーリングしているように感じるかもしれませんが、実際にはそうではありません。ポーリングするときは、積極的に何かをチェックしていて、CPUを絶えず焼き尽くしている「回転」状態を回避するのを待っています(ただし、待たない こともあります)。

エントリをチェックするときにポーリングシナリオで何が起こるかを考えてから、500ミリ秒待ちます。キューに何も存在せず、200ミリ秒後にメッセージが到着した場合、メッセージを取得するためにポーリングするときにさらに300ミリ秒待機する必要があります。タイムアウトを使用すると、メッセージが「wait」メソッドのタイムアウトの200ミリ秒後に到着すると、メッセージはすぐに処理されます。

ポーリング時の待機とタイトループでのポーリング時の一定の高いCPUによって強制される時間遅延が、ポーリングが不十分な場合が多い理由です。タイムアウトで待機することには、そのような欠点はありません。唯一のトレードオフは、スレッドが停止する前にタイムアウトが期限切れになるのを待つ必要があることです。

于 2012-09-18T00:37:09.313 に答える
8

Abort フラグを設定して、スレッドを終了する必要があることを伝えます。ServiceBroker キューにダミー レコードを追加します。その後、WAITFOR が返されます。次に、スレッドは「中止」フラグをチェックし、フラグが設定されていることを確認すると、キューからダミー レコードを削除して終了します。

もう 1 つの方法は、ServiceBroker によって監視されるテーブルの仕様に「実際の」ポイズン ピル レコード (不正なレコード番号など) を追加することです。これは、直接的な方法でスレッドに触れることをまったく回避します-常に良いことです:)これは、特に各ワークスレッドが実際の終了時に通知することが予想される場合、より複雑になる可能性がありますが、ワークスレッドがServiceBroker と DB はすべて別のボックスにありました。スレッドが通常経由でのみ通信する場合、結局のところ、もう少し考えてみると、より柔軟に見えるので、これを編集として追加しました。DBだけでシャットダウンしてみませんか?Abort() も Interrupt() も、できればロックアップを生成する Join() もありません。

于 2012-09-18T01:06:21.607 に答える
4

スレッドを強制終了する代わりに、短いタイムアウトでWAITFORを使用するようにコードを変更してください。

タイムアウトが経過したら、スレッドが中断されていないかどうかを確認してください。

そうでない場合は、ループバックしてもう一度待ちます。

はい、waitforの「要点」は何かを待つことです。ただし、何かをレスポンシブにしたい場合は、1つのスレッドにInfinityを待機するように要求してから、他のスレッドが他のスレッドをリッスンすることを期待することはできません。

于 2012-09-18T00:37:57.247 に答える
4

これをしないでください!真剣に!

スレッドを強制終了するために呼び出す必要があるTerminateThread関数は、P/Invoke を介して呼び出すことができる関数です。この方法を使用すべきでない理由はすべて、ドキュメントに記載されています。

TerminateThread は、最も極端な場合にのみ使用する必要がある危険な関数です。TerminateThread を呼び出す必要があるのは、ターゲット スレッドが何を行っているかを正確に把握しており、ターゲット スレッドが終了時に実行している可能性のあるすべてのコードを制御している場合のみです。たとえば、TerminateThread は次の問題を引き起こす可能性があります。

  • ターゲット スレッドがクリティカル セクションを所有している場合、クリティカル セクションは解放されません。
  • ターゲット スレッドがヒープからメモリを割り当てている場合、ヒープ ロックは解放されません。
  • ターゲット スレッドが終了時に特定の kernel32 呼び出しを実行している場合、スレッドのプロセスの kernel32 状態が矛盾する可能性があります。
  • ターゲット スレッドが共有 DLL のグローバル状態を操作している場合、DLL の状態が破壊され、DLL の他のユーザーに影響を与える可能性があります。

注意すべき重要な点は、太字で示した部分です。CLR / .Net フレームワークでは、ターゲット スレッドが何を行っているかを正確に把握している状況には決してなりません (たまたま CLR を作成しない限り)。

明確にするために、.Net コードを実行しているスレッドで TerminateThread を呼び出すと、プロセスがデッドロックするか、完全に回復不能な状態になる可能性があります

接続を中止する方法が見つからない場合は、TerminateThread. 他の人は、これを達成する方法について、別の提案をすでに投稿しています。


このThread.Abortメソッドは、スレッドをすぐに破棄するのではなく、を発生させるという点で少しThreadAbortException安全ですが、これには常に機能するとは限らないという欠点があります。CLR は、CLR が実際にそのスレッドでコードを実行している場合にのみ例外をスローできますが、この場合はスレッドはおそらく、何らかの IO 要求がネイティブの SQL Server クライアント コードで完了するのを待っている可能性があります。これが、への呼び出しがThread.Abort何も実行されず、制御が CLR に返されるまで何も実行されない理由です。

Thread.Abortとにかく独自の問題があり、一般的には悪いことだと考えられていますが、おそらくプロセスを完全にホースすることはありません(ただし、実行中のコードが何をしているかによっては、それでもそうなる可能性があります)。

于 2012-09-18T13:39:38.343 に答える
2

スレッドをすぐに終了するのは簡単ではありません。これに関連する潜在的な潜在的な問題があります。

スレッドはロックを取得し、ロックを解放する前にスレッドを強制終了します。これで、ロックを必要とするスレッドがスタックします。

グローバル変数を使用して、スレッドに停止を指示できます。スレッドコードで手動でグローバル変数を確認し、停止する必要があることを示している場合は戻る必要があります。

同じことについて議論しているこの質問を参照してください: C#でスレッドを即座に強制終了する方法は?

于 2012-09-18T00:20:25.543 に答える