9

入力セットに対して2つの計算を実行するJavaメソッドがあります。推定された答えと正確な答えです。見積もりは常に安価で信頼できる時間で計算できます。正確な答えは、許容できる時間内に計算できる場合とそうでない場合があります(事前にわかっていない...試してみる必要があります)。

私が設定したいのは、正確な答えに時間がかかりすぎる場合(固定タイムアウト)、代わりに事前に計算された見積もりが使用されるフレームワークです。これにはスレッドを使うと思いました。主な問題は、正確な答えを計算するためのコードが外部ライブラリに依存しているため、割り込みサポートを「注入」できないことです。

この問題のスタンドアロンのテストケースがここにあり、私の問題を示しています。

package test;

import java.util.Random;

public class InterruptableProcess {
    public static final int TIMEOUT = 1000;

    public static void main(String[] args){
        for(int i=0; i<10; i++){
            getAnswer();
        }
    }

    public static double getAnswer(){
        long b4 = System.currentTimeMillis();
        // have an estimate pre-computed
        double estimate = Math.random();

        //try to get accurate answer
        //can take a long time
        //if longer than TIMEOUT, use estimate instead
        AccurateAnswerThread t = new AccurateAnswerThread();
        t.start();

        try{
            t.join(TIMEOUT);
        } catch(InterruptedException ie){
            ;
        }

        if(!t.isFinished()){
            System.err.println("Returning estimate: "+estimate+" in "+(System.currentTimeMillis()-b4)+" ms");
            return estimate;
        } else{
            System.err.println("Returning accurate answer: "+t.getAccurateAnswer()+" in "+(System.currentTimeMillis()-b4)+" ms");
            return t.getAccurateAnswer();
        }

    }

    public static class AccurateAnswerThread extends Thread{
        private boolean finished = false;
        private double answer = -1;

        public void run(){
            //call to external, non-modifiable code
            answer = accurateAnswer();
            finished = true;
        }

        public boolean isFinished(){
            return finished;
        }

        public double getAccurateAnswer(){
            return answer;
        }

        // not modifiable, emulate an expensive call
        // in practice, from an external library
        private double accurateAnswer(){
            Random r = new Random();
            long b4 = System.currentTimeMillis();
            long wait = r.nextInt(TIMEOUT*2);

            //don't want to use .wait() since
            //external code doesn't support interruption
            while(b4+wait>System.currentTimeMillis()){
                ;
            }
            return Math.random();
        }
    }
}

これは正常に出力されます...

Returning estimate: 0.21007465651836377 in 1002 ms
Returning estimate: 0.5303547292361411 in 1001 ms
Returning accurate answer: 0.008838428149438915 in 355 ms
Returning estimate: 0.7981717302567681 in 1001 ms
Returning estimate: 0.9207406241557682 in 1000 ms
Returning accurate answer: 0.0893839926072787 in 175 ms
Returning estimate: 0.7310211480220586 in 1000 ms
Returning accurate answer: 0.7296754467596422 in 530 ms
Returning estimate: 0.5880164300851529 in 1000 ms
Returning estimate: 0.38605296260291233 in 1000 ms

ただし、分析を実行するための非常に大きな入力セット(数十億アイテムのオーダー)があり、終了しないスレッドをクリーンアップする方法がわかりません(バックグラウンド)。

スレッドを破棄するさまざまな方法が、正当な理由で非推奨になっていることを私は知っています。また、スレッドを停止する一般的な方法は、割り込みを使用することであることも知っています。run()ただし、この場合、メソッドが外部ライブラリに1回の呼び出しを渡すため、割り込みを使用できることがわかりません。

この場合、どうすればスレッドを強制終了/クリーンアップできますか?

4

3 に答える 3

2

次のような外部ライブラリについて十分に知っている場合:

  1. ロックを取得することはありません。
  2. ファイル/ネットワーク接続を開くことはありません。
  3. ロギングさえも含めて、I/Oを一切含まない。

そうすれば安全に使用できるかもしれませThread#stopん。あなたはそれを試して、広範なストレステストを行うことができます。リソースリークはすぐに明らかになるはずです。

于 2013-01-14T18:51:58.217 に答える
2

Thread.interrupt()に応答するかどうかを確認してみます。もちろん、データが永久に実行されないようにデータを減らしますが、interrupt()に応答する場合は、家が空いています。それらが何かをロックする場合、wait()、またはsleep()を実行すると、コードはInterruptedExceptionを処理する必要があり、作成者が正しいことを行った可能性があります。彼らはそれを飲み込んで続けるかもしれませんが、そうしなかった可能性があります。

技術的にはThread.stop()を呼び出すことができますが、そのコードが安全であり、リソースをリークしないかどうかを確認するには、そのコードに関するすべてを知る必要があります。ただし、その調査を行うと、コードを簡単に変更してinterrupt()も検索する方法がわかります。確実に知るためには、ソースコードを監査する必要があります。つまり、Thread.stop()を安全に呼び出すことができるかどうかを知るための調査をあまり必要とせずに、正しいことを簡単に実行してチェックを追加できます。

もう1つのオプションは、スレッドでRuntimeExceptionを発生させることです。参照を無効にするか、IO(ソケット、ファイルハンドルなど)を閉じてみてください。サイズを変更するか、データをnullにすることにより、ウォークオーバーするデータの配列を変更します。例外をスローさせるためにできることがありますが、それは処理されず、シャットダウンします。

于 2013-01-14T18:58:46.643 に答える
1

chubbsondubsによる回答を拡張して、サードパーティライブラリが明確に定義されたAPI(java.util.Listまたはライブラリ固有のAPIなど)を使用して入力データセットにアクセスする場合、サードパーティに渡す入力データセットをラップできます-フラグが設定されList.getた後、メソッドなどで例外をスローするラッパークラスを持つパーティコード。cancel

たとえば、Listをサードパーティのライブラリに渡すと、次のように実行できる可能性があります。

class CancelList<T> implements List<T> {
  private final List<T> wrappedList;
  private volatile boolean canceled = false;

  public CancelList(List<T> wrapped) { this.wrappedList = wrapped; }

  public void cancel() { this.canceled = true; }

  public T get(int index) {
    if (canceled) { throw new RuntimeException("Canceled!"); }
    return wrappedList.get(index);
  }

  // Other List method implementations here...
}

public double getAnswer(List<MyType> inputList) {
  CancelList<MyType> cancelList = new CancelList<MyType>(inputList);
  AccurateAnswerThread t = new AccurateAnswerThread(cancelList);
  t.start();

  try{
    t.join(TIMEOUT);
  } catch(InterruptedException ie){
    cancelList.cancel();
  }

  // Get the result of your calculation here...
}

もちろん、このアプローチはいくつかのことに依存します。

  1. サードパーティのコードを十分に理解している必要があります。入力パラメータを介して制御できる、サードパーティのコードが呼び出すメソッドを十分に理解している必要があります。
  2. サードパーティのコードは、計算プロセス全体でこれらのメソッドを頻繁に呼び出す必要があります(つまり、すべてのデータを一度に内部構造にコピーしてそこで計算を行うと機能しません)。
  3. 明らかに、ライブラリが実行時例外をキャッチして処理し、処理を続行する場合、これは機能しません。
于 2013-01-14T19:22:34.963 に答える