2017-11-18 23 views
0

私のコードでは、可能なゲームの動きの木に深く入り、何が最良の動きになるかを判断するために、再帰と並列ストリーム処理を頻繁に使用するタスクを実行する必要があります。これには多くの時間がかかりますので、ユーザーがコンピュータが「1000ミリ秒」などの時間を設定したいと思うのを待つのを待たされるのを防ぐためです。最高の動きが1000ミリ秒で見つからない場合、コンピュータはランダムな動きをするでしょう。 私の問題は、私は未来をキャンセルする(trueに設定されている割り込みで)と呼びますが、タスクが中断されず、ビジースレッドがバックグラウンドで実行され続けることです。 私は定期的に現在のisInterrupted()をチェックしてから救済しようとしましたが、これは役に立ちませんでした。 アイデアマルチスレッド・ビジー・タスクの未来を取り消すにはどうしたらいいですか?

public Move bestMove() { 
    ExecutorService executor = Executors.newSingleThreadExecutor(); 
    Callable<Move> callable =() -> bestEntry(bestMoves()).getKey(); 
    Future<Move> future = executor.submit(callable); 
    try { 
     return future.get(1000, TimeUnit.MILLISECONDS); 
    } catch (InterruptedException e) { 
     System.exit(0); 
    } catch (ExecutionException e) { 
     throw new RuntimeException(e); 
    } catch (TimeoutException e) { 
     future.cancel(true); 
     return randomMove(); 
    } 
    return null; 
} 

private Move randomMove() { 
    Random random = new Random(); 
    List<Move> moves = state.possibleMoves(); 
    return moves.get(random.nextInt(moves.size())); 
} 

private <K> Map.Entry<K, Double> bestEntry(Map<K, Double> map) { 
    List<Map.Entry<K, Double>> list = new ArrayList<>(map.entrySet()); 
    Collections.sort(list, (e1, e2) -> (int) (e2.getValue() - e1.getValue())); 
    return list.get(0); 
} 

private <K> Map.Entry<K, Double> worstEntry(Map<K, Double> map) { 
    List<Map.Entry<K, Double>> list = new ArrayList<>(map.entrySet()); 
    Collections.sort(list, (e1, e2) -> (int) (e1.getValue() - e2.getValue())); 
    return list.get(0); 
} 

private Map<Move, Double> bestMoves() { 
    Map<Move, Double> moves = new HashMap<>(); 
    state.possibleMoves().stream().parallel().forEach(move -> { 
     if (!Thread.currentThread().isInterrupted()) { 
      Game newState = state.playMove(move); 
      Double score = newState.isTerminal() ? newState.utility() 
        : worstEntry(new (newState).worstMoves()).getValue(); 
      moves.put(move, score); 
     } 
    }); 
    return moves; 
} 

private Map<Move, Double> worstMoves() { 
    Map<Move, Double> moves = new HashMap<>(); 
    state.possibleMoves().stream().parallel().forEach(move -> { 
     if (!Thread.currentThread().isInterrupted()) { 
      Game newState = state.playMove(move); 
      Double score = newState.isTerminal() ? -newState.utility() 
        : bestEntry(new (newState).bestMoves()).getValue(); 
      moves.put(move, score); 
     } 
    }); 
    return moves; 
} 

PS:

以下は私のコードである私もせずに試してみました「パラレル()」が、再び、単一のスレッドが実行したまま残っています。

ありがとうございます。

+0

https://techblog.bozho.net/interrupting-executor-tasks/ – MarianP

答えて

0

Future.cancelはちょうど、あなたのコードは以下のように扱う必要があり、interruptedとしてスレッドを設定します。

public static void main(String[] args) throws InterruptedException { 

    final ExecutorService executor = Executors.newSingleThreadExecutor(); 
    final Future<Integer> future = executor.submit(() -> count()); 
    try { 
     System.out.println(future.get(1, TimeUnit.SECONDS)); 
    } catch (Exception e){ 
     future.cancel(true); 
     e.printStackTrace(); 
    }finally { 
     System.out.printf("status=finally, cancelled=%s, done=%s%n", future.isCancelled(), future.isDone()); 
     executor.shutdown(); 
    } 

} 

static int count() throws InterruptedException { 
    while (!Thread.interrupted()); 
    throw new InterruptedException(); 
} 

あなたは、スレッドが実行して保つために利用可能な場合countチェックキープ見ることができるように、あなたがいることを理解する必要があります実際には、実行しているスレッドが停止してもかまわないという保証はありません。

参考:


UPDATE 2017年11月18日23:22

私は能力を持っているFutureTask拡張を書きましたストップしようとするたとえコードがinterruptシグナルを尊重しなくても、スレッドを実行することができます。 Thread.stopメソッドis deprecatedが動作していて、実際に使用する必要がある場合は安全ではないことに注意してください(たとえば、ロックを使用している場合はThread.stopの非推奨事項を読んでから、.stopデッドロックを引き起こす可能性があります)。

テストコード

public static void main(String[] args) throws InterruptedException { 
    final ExecutorService executor = newFixedSizeExecutor(1); 
    final Future<Integer> future = executor.submit(() -> count()); 
    try { 
     System.out.println(future.get(1, TimeUnit.SECONDS)); 
    } catch (Exception e){ 
     future.cancel(true); 
     e.printStackTrace(); 
    } 
    System.out.printf("status=finally, cancelled=%s, done=%s%n", future.isCancelled(), future.isDone()); 
    executor.shutdown(); 
} 

static int count() throws InterruptedException { 
    while (true); 
} 

カスタムエグゼキュータは

static ThreadPoolExecutor newFixedSizeExecutor(final int threads) { 
    return new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()){ 
     protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { 
      return new StoppableFutureTask<>(new FutureTask<>(callable)); 
     } 
    }; 
} 

static class StoppableFutureTask<T> implements RunnableFuture<T> { 

    private final FutureTask<T> future; 
    private Field runnerField; 

    public StoppableFutureTask(FutureTask<T> future) { 
     this.future = future; 
     try { 
      final Class clazz = future.getClass(); 
      runnerField = clazz.getDeclaredField("runner"); 
      runnerField.setAccessible(true); 
     } catch (Exception e) { 
      throw new Error(e); 
     } 
    } 

    @Override 
    public boolean cancel(boolean mayInterruptIfRunning) { 
     final boolean cancelled = future.cancel(mayInterruptIfRunning); 
     if(cancelled){ 
      try { 
       ((Thread) runnerField.get(future)).stop(); 
      } catch (Exception e) { 
       throw new Error(e); 
      } 
     } 
     return cancelled; 
    } 

    @Override 
    public boolean isCancelled() { 
     return future.isCancelled(); 
    } 

    @Override 
    public boolean isDone() { 
     return future.isDone(); 
    } 

    @Override 
    public T get() throws InterruptedException, ExecutionException { 
     return future.get(); 
    } 

    @Override 
    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 
     return future.get(timeout, unit); 
    } 

    @Override 
    public void run() { 
     future.run(); 
    } 
} 

出力

java.util.concurrent.TimeoutException 
    at java.util.concurrent.FutureTask.get(FutureTask.java:205) 
    at com.mageddo.spark.sparkstream_1.Main$StoppableFutureTask.get(Main.java:91) 
    at com.mageddo.spark.sparkstream_1.Main.main(Main.java:20) 
status=finally, cancelled=true, done=true 

Process finished with exit code 0 
0

回答ありがとうございました。私はもっ​​と簡単な解決策を見つけたと思う。 まず、future.cancel(true)がうまくいかなかったのは、タスクを開始したスレッドで割り込みフラグが設定されている可能性が高いからです。 (つまり、未来に関連付けられているスレッド)。 しかし、タスク自体が並列ストリーム処理を使用しているため、中断されることのない別のスレッドのワーカーが生成されるため、定期的にisInterrupted()フラグをチェックすることができません。 私が見つけた "解決策"(またはそれ以上の回避策)は、アルゴリズムのオブジェクトに自分自身の中断フラグを保持し、タスクがキャンセルされたときに手動でtrueに設定することです。すべてのスレッドが同じインスタンス上で動作するため、すべてのスレッドは中断されたフラグにアクセスし、それらはすべて従います。

関連する問題