Дождитесь завершения 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 я не могу проследить. Я проверил все переменные в своем собственном коде,и ничто не кажется пустым. Возможно, я неверно истолковываю задача.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 единиц, ожидающих a путь. Каждый игровой цикл 1 блок получает запрос пути. Я не уверен, что это как-то повлияет на то, что я здесь делаю.
2 ответа:
Если ваш поиск пути
Runnableработает нормально, когда он выполняется синхронно, но не когда он выполняется асинхронно (ваш второй пример). Я полагаю, что ваша проблема находится внутри вашегоRunnable, вы пытаетесь получить доступ к некоторому ресурсу, который является нулевым в момент вашего доступа к нему.У вас в основном есть условие гонки между строкой кода, которая обнуляет(или распоряжается) ресурсом, и кодом внутри Runnable, который должен получить к нему доступ. Другими словами, когда вы запускаете его синхронно, запускаемый всегда "добирается туда первым", так как остальная часть программы ждет его завершения. В вашем случае, когда он выполняется асинхронно, код удаления/обнуления выполняется до того, как запускаемый объект попадает в ресурс.
Вы обрезали трассировку стека, но в последней строке упоминается Метод
perform, вGoTo.javaВы делаете что-то с некоторыми ресурсами, извне Runnable, что означает, что во время выполнения runnable пытается получить доступ к чему-то, что является null.
Если вы посмотрите на Javadoc для
Future, вы увидите, чтоgetожидает завершения задачи. Если вы вместо этого проверяетеisDoneв своем игровом цикле и вызываетеgetтолько один раз, который возвращаетtrue, то вы будете работать асинхронно. Вот почему второй блок кода работает эффективно однопоточно.В следующем примере это лучше и будет выполняться асинхронно, но вы вызываете это каждый раз, когда цикл, это означает, что вы закрыли
ExecutorService, но задача все еще выполняется, так что вы затем снова вызовитеisDoneиget. Этот второй раз, вероятно, является тем, что порождаетNullPointerException. Скорее всего, вам нужно что-то, чтобы остановить его многократный вызов, например, установкаtaskв null и проверкаtask!=null && task.isDone().Вы также должны просто создать один
ExecutorServiceв начале программы, а затем использовать его повторно, а не постоянно создавать один и затем выключать его.Вам также нужно подумать об этом немного больше, например, что произойдет, если вы попытаетесь и рассчитать новый путь до завершения старого? Или изменения маршрута?