• Advertisement
Sign in to follow this  

Running a method in another thread and get result.

This topic is 872 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I want to have my pathfinding logic in a separate thread. I have little experience with threads and have tried just running a method from a new Runnable() or Thread I am currently trying it with FutureTask but without success.

 

This is the Callable where call() to run on a separate thread.

public class GetPath implements Callable {

    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 (path == null)
        {
            System.out.println("path");
        }

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

 

        

So when I give a task to a unit that requires movement this needs to be called and while calculating it should continue with the game. When it is finished it should populate a list with coordinates (the path) and work with that.

 

    
private List<Coordinate> path = new ArrayList<Coordinate>();
    FutureTask task;
    ExecutorService executor;

    public GoTo(final Creature creature, final Coordinate coordinate) {
        executor = Executors.newFixedThreadPool(2);
        //Add the GetPath task
        task = new FutureTask(new GetPath(creature.getLocation(), coordinate));
        executor.execute(task);

        //getPath(creature, coordinate); //Previous way of getting the path without threads
    }

In my update method I check if the task is done and if it's done I try to populate path. Otherwise I return since there is nothing to do without a path.

  @Override
    public boolean perform(final Creature creature) {
        if (task.isDone())
        {
            try {
                path = (List<Coordinate>)task.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            executor.shutdown();
        }
        else return false;

       //... Do something with path
  }

 

But this yields ExecutionExceptions.

java.util.concurrent.ExecutionException: java.lang.NullPointerExceptionat 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:49)
at com.buckriderstudio.buriedkingdoms.Creatures.Jobs.Hauling.perform(Hauling.java:40)
//...

If I would just use the try/catch to populate in the constructor it just waits for the task to be done and results in the same behaviour as without threading (framedrop).

 

Most of the pathfinding tasks are well within 10ms but sometimes it goes over 100ms and when multiple units require a path things slow down. I am currently stress testing this and want the game to keep running smooth while the pathfinding can take it's time to finish. I don't even care if it takes a minute to find certain paths.

Edited by menyo

Share this post


Link to post
Share on other sites
Advertisement

The problem was lying in the following line:

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

I was using this pathfinder for each thread and since this changes state I was getting errors. So I created a new pathfinder in the Runnable. Eventually when the map is dynamic I will probably need to do the same for the graph or make a deep copy of it's current state so when it the map alters during the pathfinding it will still find a (incorrect) path once the unit realizes the path is incorrect it requests a new path or cancels it's job.

Share this post


Link to post
Share on other sites

Usually one doesn't allocate a FutureTask oneself, instead you let the ExecutorService do so under the hood. I'd generally recommend that you switch to calling executor.submit(new GetPath(creature.getLocation(), coordinate)), and deal directly with Future, rather than FutureTask (which is mostly an implementation detail).

As to tracing down your error, we need the next section of the stack trace from your logs. Just after the error you posted, there should be a line that says "Caused by: java.lang.NullPointerException..." followed by a stack trace of whatever failed within your background task.

 

Edit: ninja'd by the OP :)

Edited by swiftcoder

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement