Туго зацикливается плохо?

Плохо ли зацикливание в программе?

У меня есть приложение, которое имеет два потока для симулятора игровой физики. Поток updateGame и поток рендеринга. Поток рендеринга замедляется, заставляя поток спать в течение нескольких миллисекунд (для достижения желаемой частоты кадров), а поток updateGame (который обновляет мои позиции в игровых объектах на основе некоторых уравнений физики) ранее регулировался сном в 10 миллисекунд. ,

Тем не менее, я недавно отредактировал поток updateGame, и симуляция движения моих объектов теперь выглядит значительно более реалистичной, когда я перестал спать в 10 мс. Это плохо для горячей петли или иметь плотную петлю?

private class UpdateTask implements Runnable
{
    private long previousTime = System.currentTimeMillis();
    private long currentTime = previousTime;
    private long elapsedTime;


    public void run()
    {
        while(true)
        {
        currentTime = System.currentTimeMillis();
        elapsedTime = (currentTime - previousTime); // elapsed time in seconds


        updateGame(elapsedTime / 1000f);

            try {
                Thread.currentThread().sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        previousTime = currentTime;
        }
    }
}

В этом примере я просто сплю в течение 1 мс (и из моего понимания того, как работает точность в миллисекундах и функция сна, это, вероятно, больше похоже на 5-10 мс. Если я сплю больше, чем это, это начинает оказывать влияние на точность моя модель обнаружения столкновений и физика.

Плохо ли иметь плотные петли или петли с 1 мс спящими в них? Есть ли что-то еще, что я должен сделать вместо этого?

12.12.2008 03:06:55
Вы должны использовать Thread.sleep(...)вместо, Thread.currentThread().sleep(...)потому что это статический метод; ваш код может соблазнить вас делать то, someOtherThread.sleep(...)что не спит someOtherThread.
newacct 19.09.2009 04:40:48
7 ОТВЕТОВ
РЕШЕНИЕ

Я прочитал действительно хороший пост о том, как эффективно и эффективно выполнять цикл физических вычислений: исправьте свой временной шаг!

Когда игра запущена, это, как правило, основное приложение, о котором пользователь заботится о столь жестком цикле, это не такая уж большая проблема. Что вы действительно должны делать, хотя планируйте свои обновления. Вы должны знать, сколько времени - при вашей целевой частоте кадров - что ваш кадр должен выполнить. Вы должны измерить время, которое занял ваш кадр, и только спать в течение времени, в течение которого ваш кадр занимал минус это известное время кадра. Таким образом, ваша система будет фиксировать частоту кадров и не будет зависеть от времени, которое требуется вашему кадру для рендеринга.

Другое дело, что я не верю, что Thread.sleep имеет очень хорошее разрешение, более 5 миллисекунд, возможно, вы захотите найти более точный таймер, доступный для Java.

9
12.12.2011 17:40:52
У меня есть два потока, мой поток updateGame и мой поток рендеринга. Мой поток рендеринга привязан к maxFrameRate, как вы говорите, исходя из его истекшего времени.
mmcdole 12.12.2008 03:28:44
Большой upvote для «исправить свой временной шаг». Еще в темные века мы использовали похожие методы для обоснования текста!
Norman Ramsey 13.12.2008 06:03:04

Это только «плохо», если это оказывает негативное влияние на что-то еще в вашей системе. Вместо того, чтобы спать в течение 1 мс, вы можете заблокировать условие, которое требует обновления, с минимумом 1 мс. Таким образом, вы всегда будете спать по крайней мере 1 мс и дольше, если вам нечего будет делать.

2
12.12.2008 03:11:59

Как указал Адам в своем ответе, это может отрицательно сказаться на производительности системы.

Я также пытался создавать игры очень похожим образом (имея расчеты рендеринга и движения в отдельных потоках) и обнаружил, что отсутствие Thread.sleepэтого заставит приложение Java отнимать очень значительную часть процессорного времени.

Еще одна вещь, которую следует учитывать, - это сам системный таймер Как вы упомянули, хотя этот Thread.sleepметод занимает несколько миллисекунд для сна, но эта точность зависит (как указано в спецификациях API) от таймера, предоставляемого операционной системой. В случае операционных систем на базе Windows NT разрешение таймера составляет 10 миллисекунд . (См. Также: System.currentTimeMillis против System.nanoTime )

Да, это правда, что наличие Thread.sleepможет снизить производительность вашего приложения, но отсутствие этого может привести к стремительному росту использования системы приложением.

Я полагаю, что решение сводится к тому, должно ли приложение занимать значительную часть использования системы, или вести себя хорошо и делить процессорное время с другими приложениями, работающими в системе.

2
23.05.2017 12:17:57

Для игры, вероятно, нет. Просто убедитесь, что ваша игра останавливается, когда переключает задачи.

0
12.12.2008 03:30:07

Вы действительно хотите использовать Thread.yield () в этом случае. Возможно, что один поток будет работать непрерывно, и не будет позволять другим потокам времени выполняться. Выполнение вызова yield в конце каждой итерации дает планировщику подсказку о том, что пришло время и другим потокам работать.

0
12.12.2008 04:05:52

Также обратите внимание на пользователей ноутбуков: непрерывный цикл будет держать процессор перегруженным, и это будет разряжать их батарею (многие флэш-игры виноваты в этом). Что-то, чтобы рассмотреть, решая, душить ли Ваши петли или нет.

1
13.01.2009 04:08:11

Ответ от joshperry - это почти то, что вы хотите, но есть и несколько способов. Если вы используете несколько потоков, вам также придется иметь дело с блокировкой и т. Д. В зависимости от вашей игровой архитектуры, это может / не может иметь большого значения. Например, выполняете ли вы много блокировок, много ли сообщений передается между потоками и т. Д. Если вы играете в традиционную игру, у вас обычно есть один основной цикл - у меня есть очередь объектов CMD (запускается, если хотите, но может также больше событий (как в природе), которые выполняются непрерывно до тех пор, пока очередь не станет пустой. Затем поток ожидает, пока не будет сообщено, что новый cmd находится в очереди. Для большинства игр этого обычно достаточно. Тогда возникает вопрос, как / когда добавляются cmds. Я использую таймер / планировщик (также обратите внимание на комментарии о разрешении времени Java), чтобы добавить cmd в основной цикл с требуемой частотой кадров. Это имеет преимущество в том, что вы также любезны по отношению к ноутбукам и т. Д. При запуске вы также можете сравнить систему, чтобы увидеть, насколько быстро она работает, а затем установить соответствующую частоту кадров (т. Е. Начать с поддерживаемой базы, а затем работать на макс. ). Сравнительный анализ или использование заданных пользователем советов по производительности (т. Е. Количество деталей рендеринга) могут затем использоваться каждым типом cmd (т. Е. Cmd / событие render scence просматривает параметры производительности для деталей и т. Д.). (примечание - cmds не должен быть запущен, они могут быть больше похожи на шину событий со слушателями, которые вызываются в основном потоке). При запуске вы также можете сравнить систему, чтобы увидеть, насколько быстро она работает, а затем установить соответствующую частоту кадров (т. Е. Начать с поддерживаемой базы, затем работать на максимуме). Сравнительный анализ или использование заданных пользователем советов по производительности (т. Е. Количество деталей рендеринга) могут затем использоваться каждым типом cmd (т. Е. Cmd / событие render scence просматривает параметры производительности для деталей и т. Д.). (примечание - cmds не должен быть запущен, они могут быть больше похожи на шину событий со слушателями, которые вызываются в основном потоке). При запуске вы также можете сравнить систему, чтобы увидеть, насколько быстро она работает, а затем установить соответствующую частоту кадров (т. Е. Начать с поддерживаемой базы, затем работать на максимуме). Сравнительный анализ или использование заданных пользователем советов по производительности (т. Е. Количество деталей рендеринга) могут затем использоваться каждым типом cmd (т. Е. Cmd / событие render scence просматривает параметры производительности для деталей и т. Д.). (примечание - cmds не должен быть запущен, они могут быть больше похожи на шину событий со слушателями, которые вызываются в основном потоке).

Кроме того, если задача хочет затем использовать многопоточный / основной обработчик для cmd (если это модель типа события - мне лично нравится модель события - проще получить доступ к информации общего состояния без необходимости использования глобальных синглетонов), то можно создать несколько задач (скажем, с использованием существующего пула потоков - чтобы стоимость новых потоков не попадала в каждый cmd), а затем используйте класс типа барьера, чтобы дождаться завершения всех задач. Этот метод обычно упрощает блокировку, поскольку каждый cmd (или система) обычно предъявляет различные требования к блокировке. Таким образом, вы можете реализовать только блокировку для этой системы и не беспокоиться о блокировке между подсистемами - т.е. для физики вы можете заблокировать связки объектов в игровой зоне, и каждый разветвленный поток в пуле потоков будет заботиться только о своих объектах, т.е. нить1 обрабатывает объекты с 1 по 20,

Важно посмотреть, как и почему вы используете потоки, блокировки и т. Д.

1
18.03.2009 05:41:41