1

ThreadPoolExecutorを使用して作成されたタスクの実行時間を制限する方法に関する詳細情報を見つけようとしています。

たとえば、時間が経過すると (たとえば 1m)、スレッドは自動的に終了し、null 値を返します。ここでの重要な点は、スレッドの終了を待機することでメイン スレッド (この例では UI スレッド) をブロックしてはならないということです。

getメソッドを使用できることはわかっていますが、アプリケーションがブロックされます。

1分間スリープしてからメインスレッドで割り込みを呼び出す追加の内部スレッドを実行することを考えていました。

サンプル コードを添付しました。これは良いアイデアのように見えますが、それが理にかなっている場合は別の目で確認する必要があります。

public abstract class AbstractTask<T> implements Callable<T> {
private final class StopRunningThread implements Runnable {
    /**
     * Holds the main thread to interrupt. Cannot be null.
     */
    private final Thread mMain;

    public StopRunningThread(final Thread main) {
        mMain = main;

    }
    @Override
    public void run() {
        try {
            Thread.sleep(60 * 1000);
            // Stop it.
            mMain.interrupt();
        } catch (final InterruptedException exception) {
            // Ignore.
        }
    }
}

call() は ThreadPool 経由で呼び出されます

public T call() {
    try {
        // Before running any task initialize the result so that the user
        // won't
        // think he/she has something.
        mResult = null;
        mException = null;
        // Stop running thread.
        mStopThread = new Thread(new StopRunningThread(
                Thread.currentThread()));
        mStopThread.start();

        mResult = execute(); <-- A subclass implements this one
    } catch (final Exception e) {
        // An error occurred, ignore any result.
        mResult = null;
        mException = e;
        // Log it.
        Ln.e(e);
    }
    // In case it's out of memory do a special catch.
    catch (final OutOfMemoryError e) {
        // An error occurred, ignore any result.
        mResult = null;
        mException = new UncheckedException(e);
        // Log it.
        Ln.e(e);
    } finally {
        // Stop counting.
        mStopThread.interrupt();
    }

    return mResult;
}

私が恐れている点がいくつかあります:

  • execute() に例外があり、その直後に外部スレッドが中断された場合、例外をキャッチすることはありません。
  • メモリ/CPU の消費量。スレッド プールを使用して、新しいスレッドの作成を回避しています。

同じ機能を達成するためのより良いアイデアはありますか?

4

1 に答える 1

1

これを行うには多少複雑になります。まず、ThreadPoolExecutorクラスを拡張する必要があります。「beforeExecute」メソッドと「afterExecute」メソッドをオーバーライドする必要があります。彼らはスレッドの開始時間を追跡し、その後クリーンアップを行います。次に、どのスレッドをクリーンアップする必要があるかを定期的に確認するための刈り取り機が必要になります。

この例では、マップを使用して、各スレッドがいつ開始されたかを記録します。beforeExecuteメソッドがこれにデータを入力し、afterExecuteメソッドがこれをクリーンアップします。現在のすべてのエントリ(つまり、実行中のすべてのスレッド)を定期的に実行して確認し、指定された制限時間を超えたすべてのエントリに対してThread.interrupt()を呼び出すTimerTaskがあります。

2つの追加のコンストラクターパラメーターを指定したことに注意してください。maxExecutionTimeとreaperIntervalは、タスクが与えられる時間と、強制終了するタスクをチェックする頻度を制御します。簡潔にするために、ここではいくつかのコンストラクターを省略しました。

あなたが提出するタスクはうまくプレイし、自分自身を殺すことができるようにする必要があることを覚えておいてください。これは、次のことを行う必要があることを意味します。

  1. 実行中は定期的にThread.currentThread()。isInterrupted()を確認してください。
  2. throws句でInterruptedExceptionを宣言しないブロッキング操作は避けてください。この典型的な例は、InputStream / OutputStreamの使用法であり、代わりにNIOチャネルを使用します。これらの方法を使用する必要がある場合は、そのような操作から戻った直後に中断フラグを確認してください。

public class TimedThreadPoolExecutor extends ThreadPoolExecutor {
    private Map<Thread, Long> threads = new HashMap<Thread, Long>();
    private Timer timer;

    public TimedThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
            long maxExecutionTime,
            long reaperInterval) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        startReaper(maxExecutionTime, reaperInterval);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        threads.remove(Thread.currentThread());
        System.out.println("after: " + Thread.currentThread().getName());
        super.afterExecute(r, t);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        System.out.println("before: " + t.getName());
        threads.put(t, System.currentTimeMillis());
    }

@Override
protected void terminated() {
    if (timer != null) {
        timer.cancel();
    }
    super.terminated();
}

    private void startReaper(final long maxExecutionTime, long reaperInterval) {
        timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // make a copy to avoid concurrency issues.
                List<Map.Entry<Thread, Long>> entries = 
                        new ArrayList<Map.Entry<Thread, Long>>(threads.entrySet());
                for (Map.Entry<Thread, Long> entry : entries) {
                    Thread thread = entry.getKey();
                    long start = entry.getValue();
                    if (System.currentTimeMillis() - start > maxExecutionTime) {
                        System.out.println("interrupting thread : " + thread.getName());
                        thread.interrupt();
                    }
                }
            }

        };
        timer.schedule(timerTask, reaperInterval, reaperInterval);
    }

    public static void main(String args[]) throws Exception {
        TimedThreadPoolExecutor executor = new TimedThreadPoolExecutor(5,5, 1000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(20),
                1000L,
                200L);

        for (int i=0;i<10;i++) {
            executor.execute(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(5000L);
                    }
                    catch (InterruptedException e) {

                    }
                }
            });
        }

        executor.shutdown();
        while (! executor.isTerminated()) {
            executor.awaitTermination(1000L, TimeUnit.MILLISECONDS);
        }
    }



}
于 2012-05-27T04:58:19.067 に答える