Дождитесь завершения 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
в начале программы, а затем использовать его повторно, а не постоянно создавать один и затем выключать его.Вам также нужно подумать об этом немного больше, например, что произойдет, если вы попытаетесь и рассчитать новый путь до завершения старого? Или изменения маршрута?