等待游戏循环中的FutureTask完成

时间:2015-12-04 13:24:09

标签: java multithreading concurrency libgdx futuretask

我想为昂贵的路径查找任务设置FutureTask。所以我创建了这个Callable类。

public class GetPath implements Callable<List<Coordinate>> {

    private Coordinate start;
    private Coordinate end;

    public GetPath(Coordinate start, Coordinate end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public List<Coordinate> call() throws Exception {

        IndexedNodeImplementation location = TestMap.mapgraph.getNodeByCoord(start.x, start.y);
        IndexedNodeImplementation destination = TestMap.mapgraph.getNodeByCoord(end.x, end.y);
        GraphPathImplementation resultPath = new GraphPathImplementation();

        List<Coordinate> path = new ArrayList<Coordinate>();

        TestMap.pathFinder.searchNodePath(location, destination, new HeuristicImplementation(), resultPath);

        if (resultPath.getCount() > 0)
        {
            for (int i = 1; i < resultPath.getCount(); i++)
            {
                path.add(new Coordinate(resultPath.get(i).origin(), true));
            }
        }
        return path;
    }
}

当我在一个示例中执行此操作时,我在构造函数中看到它仍然“等待”直到任务完成。虽然这有效,但我仍然会遇到帧丢失,就好像我只是在没有线程的情况下那样。

executor = Executors.newFixedThreadPool(1);
task = new FutureTask(new GetPath(creature.getLocation(), coordinate));
//TestMap.executor.execute(task);

executor.execute(task);

try {
    path = (List<Coordinate>)task.get();
    } catch (InterruptedException e) {
        System.out.println("Interrupted Exception");
        e.printStackTrace();
    } catch (ExecutionException e) {
        System.out.println("Execution Exception"); // <---
        e.printStackTrace();
    }

    executor.shutdown();
    //Works but is basically the same as just looking up a path in the main thread.

我知道拥有一个线程池是非常愚蠢的,但我是新手。无论如何,我尝试为每个对象使用相同的ExecutorService更大的池,但没有成功。

由于此方法仍然保持程序直到完成,我尝试在游戏循环中观看FutureTask。完成后,我填充路径列表并继续使用代码。

        if (task.isDone())
        {
            try {
                path = (List<Coordinate>)task.get();
            } catch (InterruptedException e) {
                System.out.println("Interrupted Exception");
                e.printStackTrace();
            } catch (ExecutionException e) {
                System.out.println("Execution Exception");
                e.printStackTrace();
            }
            executor.shutdown();
            //TestMap.executor.shutdown();
        }
        else
        {
            return false;
        }
        //Continue with code and work with path which should now contain elements

但这会给NullPointerException我无法追踪。我已经检查了自己的代码中的所有变量,似乎没有任何变量。也许我误解了task.isDone。或许我应该将任务留给执行者来处理和使用执行程序来确定任务是否完成但我不确定如何。

java.util.concurrent.ExecutionException: java.lang.NullPointerException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at com.buckriderstudio.buriedkingdoms.Creatures.Jobs.GoTo.perform(GoTo.java:55)
//...

我只是想在另一个线程上运行一些昂贵的代码,而不会中断我的主游戏循环。我不关心它需要多长时间才能找到一条路径,这就是为什么我宁愿把它放在一个单独的线程中,然后尝试实现一个更好的路径查找算法,如分层A *。我目前正在使用gdx.ai.pfa.*进行寻路。

打开当前程序后,有100个单位在等待路径。每个游戏循环1单元获得路径请求。我不确定这对我在这里做的事情是否有任何影响。

2 个答案:

答案 0 :(得分:1)

如果找到Runnable的路径在同步运行时工作正常,而不是在异步运行时(第二个示例)。我相信您的问题在Runnable内,您正在尝试访问某些资源,在您访问它时该资源为空。

在使资源空闲(或处置)的代码行与需要访问它的Runnable中的代码之间基本上存在竞争条件。换句话说,当你同步运行它时,Runnable始终会#34;首先到达那里,因为程序的其余部分正在等待它完成。在您的情况下,当它异步时,在Runnable到达资源之前执行disposing / nulling代码。

您剪断了堆栈跟踪,但最后一行提到了perform

中的GoTo.java方法

你对Runnable外部的一些资源做了一些事情,这意味着,在执行runnable时,它试图访问一些空的东西。

答案 1 :(得分:0)

如果查看Future的{​​{3}},您会看到get等待任务完成。如果您在游戏循环中检查isDone并且只返回get后调用true,那么您将异步运行。这就是第二个代码块有效运行单线程的原因。

在下一个示例中,它会更好并且将以异步方式运行,但每次循环时都会调用它,这意味着您关闭了ExecutorService,但任务仍然完成,因此您调用isDone并再次get。第二次可能是生成NullPointerException的原因。您很可能需要一些东西来阻止它被重复调用,例如将task设置为null并检查task!=null && task.isDone()

您还应该在程序开头创建一个ExecutorService,然后重复使用它,而不是不断创建一个,然后关闭它。

你需要多考虑一下这个问题,例如,如果你在旧路径完成之前尝试计算新路径会发生什么?或路径变化?