我应该如何在java中断和处理一组线程?

时间:2012-11-27 05:23:45

标签: java multithreading

我正在编写一个在游戏状态下执行alhpa-beta搜索的游戏引擎,我正在尝试将其并行化。到目前为止我所做的一切都在起作用,然后它似乎慢慢停下来。我怀疑这是因为我没有正确处理我的线程。

当与电脑对战时,游戏会调用MultiThreadedComputerPlayer对象的getMove()函数。以下是该方法的代码:

public void getMove(){
    int n = board.legalMoves.size();
    threadList = new ArrayList<WeightedMultiThread>();
    moveEvals = new HashMap<Tuple, Integer>();

    // Whenever a thread finishes its work at a given depth, it awaits() the other threads
    // When all threads are finished, the move evaluations are updated and the threads continue their work.
    CyclicBarrier barrier = new CyclicBarrier(n, new Runnable(){
        public void run() {
            for(WeightedMultiThread t : threadList){
                moveEvals.put(t.move, t.eval);
            }
        }
    });

    // Prepare and start the threads
    for (Tuple move : board.legalMoves) {
        MCBoard nextBoard = board.clone();
        nextBoard.move(move);
        threadList.add(new WeightedMultiThread(nextBoard, weights, barrier));
        moveEvals.put(move, 0);
    }
    for (WeightedMultiThread t : threadList) {t.start();}

    // Let the threads run for the maximum amount of time per move
    try {
        Thread.sleep(timePerMove);
    } catch (InterruptedException e) {System.out.println(e);}
    for (WeightedMultiThread t : threadList) {
        t.stop();
    }

    // Play the best move
    Integer best = infHolder.MIN;
    Tuple nextMove = board.legalMoves.get(0);
    for (Tuple m : board.legalMoves) {
        if (moveEvals.get(m) > best) {
            best = moveEvals.get(m);
            nextMove = m;
        }
    }
    System.out.println(nextMove + " is the choice of " + name + " given evals:");
    for (WeightedMultiThread t : threadList) {
        System.out.println(t);
    }
    board.move(nextMove);
}

这里有问题的线程的run()方法:

public void run() {
    startTime = System.currentTimeMillis();
    while(true) {
        int nextEval = alphabeta(0, infHolder.MIN, infHolder.MAX);
        try{barrier.await();} catch (Exception e) {}
        eval = nextEval;
        depth += 1;
    }
}

我需要能够在时间到了时中断所有线程 - 我该如何实现呢?截至目前,我一直在捕捉(并忽略)InterruptedExceptions。

3 个答案:

答案 0 :(得分:6)

Thread.stop因某种原因而被弃用。当您在中间中断线程时,线程没有机会正确释放它正在使用的资源,并且不会通知其他线程完成它...这在多线程应用程序中非常重要。我对你的表演坦克并不感到惊讶;我愿意打赌你的记忆用法在屋顶上拍摄。你也不回收线程,你可以在不创建新对象的情况下启动和停止它们,这意味着变量留下的任何破坏状态可能仍然困扰着它们。

更好的方法是设置一个标志,告诉线程它应该返回。因此,在WeightedMultiThread类中包含一个名为shouldQuit的布尔值,并在每次调用start()时将其设置为false。然后,而不是while(true)do while(!shouldQuit),而不是t.stop(),而不是t.shouldQuit = true。在对每个线程执行此操作之后,请使用另一个循环来检查每个线程的t.isAlive(),并在每个线程返回后,继续执行您的业务。你应该有更好的结果。

答案 1 :(得分:3)

这看起来像是使用ExecutorService的理想场所。您可以创建实现并行任务的Callable个实例,将其提交到ExecutorService,然后使用awaitTermination强制执行超时。

例如:

public void getMove() {
    ExecutorService service = Executors.newFixedThreadPool(board.legalMoves.size());
    List<Future<Something>> futures = new ArrayList<Future<Something>>(board.legalMoves.size());
    for (Tuple move : board.legalMoves) {
        futures.add(service.submit(new WeightedMultiThread(...)));
    }
    service.awaitTermination(timePerMove, TimeUnit.MILLISECONDS);
    service.shutdownNow(); // Terminate all still-running jobs
    for (Future<Something> future : futures) {
        if (future.isDone()) {
            Something something = future.get();
            // Add best move logic here
        }
    }
    ...
}

Something替换为封装有关已评估移动的信息的内容。我建议Something是一个包含Tuple及其相关分数的类。您的WeightedMultiThread课程可以执行以下操作:

class WeightedMultiThread implements Callable<Something> {
    public Something call() {
        // Compute score
        ...
        // Return an appropriate data structure
        return new Something(tuple, score);
    }
}

更好的方法是创建ExecutorService一次,并在每次调用getMove时重复使用它。创建线程是昂贵的,所以如果可以,最好只做一次。如果采用这种方法,则不应调用shutdownNow,而应使用Future.cancel方法终止未及时完成的​​作业。确保您的WeightedMultiThread实现检查线程中断并抛出InterruptedException。这通常是编写需要可中断的长期运行任务的好方法。

编辑:

由于您正在对游戏空间进行逐级探索,我建议您在getMove函数而不是Tuple评估代码中对其进行编码,例如< / p>

public Tuple getMove() {
    ExecutorService service = ...
    Tuple best = null;
    long timeRemaining = MAX_TIME;
    for (int depth = 0; depth < MAX_DEPTH && timeRemaining > 0; ++depth) {
        long start = System.currentTimeMillis();
        best = evaluateMoves(depth, service, timeRemaining);
        long end = System.currentTimeMillis();
        timeRemaining -= (end - start);
    }
    return best;
}

private Tuple evaluateMoves(int depth, ExecutorService service, long timeRemaining) {
    List<Future<Whatever>> futures = service.submit(...); // Create all jobs at this depth
    service.awaitTermination(timeRemaining, TimeUnit.MILLISECONDS);
    // Find best move
    ...
    return best;
}

这可能更干净,但你明白了。

答案 2 :(得分:0)

最敏感的方法是使用中断机制。 Thread.interrupt()Thread.isInterrupted()方法。这可以确保您的消息将被传递到线程,即使它位于阻塞调用内(请记住一些方法声明抛出InterruptedException?)

P.S。阅读Brian Goetz的"Java Concurrency in Practice"第7章:取消和关闭会很有用。