5

現在、既存のアプリケーションに Web サービス ベースのフロント エンドを作成しています。そのために、私はWCF LOB Adapter SDKを使用しています。これにより、外部データと操作を Web サービスとして公開するカスタム WCF バインディングを作成できます。

SDK は実装するいくつかのインターフェイスを提供し、それらのメソッドの一部は時間制限があります。実装は、指定された時間内に作業を完了するか、TimeoutExceptionをスローすることが期待されます。

調査の結果、「Implement C# Generic Timeout」という質問にたどり着きました。この質問は、ワーカー スレッドを使用することを賢明にアドバイスしています。その知識があれば、次のように書くことができます。

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex,
    int maxChildNodes, TimeSpan timeout)
{
    Func<MetadataRetrievalNode[]> work = () => {
        // Return computed metadata...
    };

    IAsyncResult result = work.BeginInvoke(null, null);
    if (result.AsyncWaitHandle.WaitOne(timeout)) {
        return work.EndInvoke(result);
    } else {
        throw new TimeoutException();
    }
}

ただし、ワーカー スレッドがタイムアウトした場合にどうするかについては、コンセンサスが明確ではありません。上記のコードのように忘れるか、中止することができます:

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex,
    int maxChildNodes, TimeSpan timeout)
{
    Thread workerThread = null;
    Func<MetadataRetrievalNode[]> work = () => {
        workerThread = Thread.CurrentThread;
        // Return computed metadata...
    };

    IAsyncResult result = work.BeginInvoke(null, null);
    if (result.AsyncWaitHandle.WaitOne(timeout)) {
        return work.EndInvoke(result);
    } else {
        workerThread.Abort();
        throw new TimeoutException();
    }
}

現在、スレッドを中止することは、広く間違っていると考えられています。進行中の作業を中断し、リソースをリークし、ロックを台無しにし、スレッドが実際に実行を停止することさえ保証しません。つまり、HttpResponse.Redirect()呼び出されるたびにスレッドを中止し、IIS はそれに完全に満足しているようです。何らかの形で対処する準備ができているのかもしれません。私の外部アプリケーションはおそらくそうではありません。

一方、リソース競合の増加 (プール内の使用可能なスレッドの減少) は別として、ワーカー スレッドにそのコースを実行させた場合、work.EndInvoke()呼び出されることはないため、とにかくメモリ リークは発生しませんか? より具体的には、MetadataRetrievalNode[]によって返された配列はwork永遠に残りませんか?

これは 2 つの悪のうち小さい方を選択するだけの問題ですか、それともワーカー スレッドを中止せずに使用されたメモリを再利用する方法はありBeginInvoke()ますか?

4

2 に答える 2

6

まあ、最初Thread.Abortは以前ほど悪くはありません。2.0 では CLR にいくつかの改良が加えられ、スレッドの中止に関するいくつかの主要な問題が修正されました。それでも悪いので、避けるのが最善の策です。スレッドを中止する必要がある場合は、少なくとも、中止が発生した場所からアプリケーション ドメインを破棄することを検討する必要があります。これは、ほとんどのシナリオで非常に侵襲的であり、管理されていないリソースの破損の可能性を解決しません。

それとは別に、この場合の中止には他の意味があります。最も重要なのは、ThreadPoolスレッドを中止しようとしているということです。その最終結果がどうなるかは本当にわかりません。使用中のフレームワークのバージョンによって異なる可能性があります。

最善の方法はFunc<MetadataRetrievalNode[]>、安全なポイントでデリゲートに変数をポーリングさせて、それ自体で実行を終了する必要があるかどうかを確認することです。

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex, int maxChildNodes, TimeSpan timeout)
{
    bool terminate = false;

    Func<MetadataRetrievalNode[]> work = 
      () => 
      {
        // Do some work.

        Thread.MemoryBarrier(); // Ensure a fresh read of the terminate variable.
        if (terminate) throw new InvalidOperationException();

        // Do some work.

        Thread.MemoryBarrier(); // Ensure a fresh read of the terminate variable.
        if (terminate) throw new InvalidOperationException();

        // Return computed metadata...
      };

    IAsyncResult result = work.BeginInvoke(null, null);
    terminate = !result.AsyncWaitHandle.WaitOne(timeout);
    return work.EndInvoke(result); // This blocks until the delegate completes.
}

トリッキーな部分は、デリゲート内でブロッキング呼び出しを処理する方法です。terminate明らかに、デリゲートがブロッキング コールの最中にある場合、フラグを確認することはできません。ただし、ブロック呼び出しが既定の BCL 待機メカニズム ( 、 など) の 1 つから開始されたと仮定すると、WaitHandle.WaitOneそれを「突く」ためにMonitor.Wait使用でき、すぐにブロックを解除する必要があります。Thread.Interrupt

于 2010-12-21T21:46:43.037 に答える
1

答えは、ワーカースレッドが実行している作業の種類によって異なります。私の推測では、データ接続などの外部リソースを使用しています。Thread.Abort()は、どのように適切にラップされていても、アンマネージリソースへのフックを使用してスレッドが機能する場合には実際に悪です。

基本的に、あなたはそれがタイムアウトした場合にあなたのサービスをあきらめたいです。この時点で、理論的には、呼び出し元はスレッドの所要時間を気にしなくなります。「長すぎる」ことだけを気にし、先に進む必要があります。ワーカースレッドの実行メソッドにバグがなければ、最終的には終了します。発信者は、もう待機していないため、いつか気になりません。

スレッドがタイムアウトした理由が無限ループに陥ったため、またはサービスコールなどの他の操作を永久に待機するように指示された場合は、修正する必要がある問題がありますが、修正は強制終了ではありませんスレッド。それはあなたが車の中で待っている間にあなたの子供を食料品店に送ってパンを買うのと同じことです。あなたの子供が5分かかると思うときに店で15分を過ごし続けると、あなたは最終的に好奇心をそそられ、入って彼らが何をしているかを調べます。彼らがいつも鍋やフライパンを見ていたように、それがあなたがすべきだと思っていたものではない場合、あなたは将来の機会のために彼らの行動を「修正」します。あなたが入って、あなたの子供が長いチェックアウトラインに立っているのを見るならば、あなたはただもっと長く待ち始めます。どちらの場合も、着用している爆発物のベストを爆発させるボタンを押すべきではありません。それはただ大きな混乱を引き起こし、後で同じ用事をする次の子供の能力を妨げる可能性があります。

于 2010-12-21T21:41:51.913 に答える