Как я могу остановить цикл Java с тем, чтобы он съел> 50% моего процессора? - PullRequest
19 голосов
/ 24 февраля 2009

Хорошо, я проверил это на пустой программе, и просто некоторое время выполнения (true) {} дало мне> 50% на моем процессоре. У меня есть игра, над которой я работаю, которая использует цикл while в качестве основного цикла, а его процессор постоянно равен 100.

Как я могу заставить Java повторять что-то снова и снова, не съедая> 50% моего процессора, просто чтобы повторять?

Ответы [ 9 ]

42 голосов
/ 24 февраля 2009

Добавить сон, чтобы перевести поток в режим ожидания на некоторый интервал:

Thread.sleep

Не имея режима сна, цикл while будет использовать все доступные вычислительные ресурсы. (Например, теоретически, 100% в одноядерной системе или 50% в двухъядерном и т. Д.)

Например, следующее будет циклически повторяться через цикл while примерно каждые 50 миллисекунд:

while (true)
{
    try
    {
        Thread.sleep(50);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

Это должно немного снизить нагрузку на процессор.

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


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

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

Потоки и другие довольно сложные области программирования, особенно когда начинаете, поэтому я предлагаю поднять другой вопрос, когда это станет необходимым.

Ниже приведен пример приложения Swing, основанного на JFrame, который обновляет JLabel, который будет содержать возвращаемое значение из System.currentTimeMillis. Процесс обновления происходит в отдельном потоке, и кнопка «Стоп» остановит поток обновления.

Несколько концепций, которые иллюстрирует пример:

  • Основанное на Swing приложение GUI с отдельным потоком для обновления времени - это предотвратит блокировку потока GUI. (Вызывается EDT или потоком отправки события в Swing.)
  • Наличие цикла while с условием цикла, которое не true, но заменено на boolean, которое будет определять, поддерживать ли цикл в рабочем состоянии.
  • Как Thread.sleep учитывает фактическое применение.

Пожалуйста, извините за длинный пример:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TimeUpdate
{
    public void makeGUI()
    {
        final JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JLabel l = new JLabel();

        class UpdateThread implements Runnable
        {
            // Boolean used to keep the update loop alive.
            boolean running = true;

            public void run()
            {
                // Typically want to have a way to get out of
                // a loop. Setting running to false will 
                // stop the loop.
                while (running)
                {
                    try
                    {
                        l.setText("Time: " +
                                System.currentTimeMillis());

                        Thread.sleep(50);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }

                // Once the run method exits, this thread
                // will terminate.
            }
        }

        // Start a new time update thread.
        final UpdateThread t = new UpdateThread();
        new Thread(t).start();

        final JButton b = new JButton("Stop");
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e)
            {
                t.running = false;
            }
        });

        // Prepare the frame.
        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(l, BorderLayout.CENTER);
        f.getContentPane().add(b, BorderLayout.SOUTH);
        f.setLocation(100, 100);
        f.pack();
        f.setVisible(true);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new TimeUpdate().makeGUI();
            }
        });
    }
}

Некоторые ресурсы о многопоточности и использовании Swing:

29 голосов
/ 24 февраля 2009

Тема. Сон не может быть полным ответом. Для большинства игр слишком много процессорного времени для необходимого объема работы. Просто спать в течение определенного количества времени не так уж и эффективно, так как вы, скорее всего, будете либо сжигать слишком много ресурсов, либо недостаточно. Обычно вы хотите как-то рассчитать время работы.

Если подумать, экран обновляется только с определенной скоростью, обычно менее 100 раз в секунду. Я не знаком с API Java для такого рода вещей, но вам нужно выяснить скорость обновления монитора, а затем обновлять только один раз между каждым обновлением.

Кроме того, вам не нужен такой цикл для основного цикла, вы можете использовать таймеры для регулярного вызова кода обновления. Это еще более эффективно.

10 голосов
/ 24 февраля 2009

Вы действительно заняты ожиданием, то есть вы постоянно шлифуете свой ЦП, проверяя одно или несколько условий, пока они не станут истинными.

Thread.sleep() не является решением. Использование методов wait() и notify() позволяет вам делать то, что вы пытаетесь, но гораздо эффективнее. По сути, этот механизм позволяет потоку спать на объекте, пока объект не решит, что что-то произошло, и не хочет разбудить все спящие на нем потоки.

Примеры кода можно найти здесь и здесь.

Это должно быть вашим решением, а не скрывать ваше занятое ожидание с таймером.

6 голосов
/ 24 февраля 2009

Хотя да, вы могли бы сделать

Thread.sleep(50)

как и в случае принятого ответа, вы также можете позвонить

Thread.sleep(0) 

Это скажет процессору сделать переключение контекста. Затем будут выполняться другие потоки, ожидающие выполнения (например, поток рисования GUI), и машина перестанет чувствовать себя медленно.

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

6 голосов
/ 24 февраля 2009

Обычно в игровом цикле вы немного задерживаетесь ... например: http://developers.sun.com/mobility/midp/articles/game/

0 голосов
/ 28 февраля 2009

Если есть прерывание, это, вероятно, по причине. Лучший способ справиться с этим -

try {
    while (true) {
        Thread.sleep(50);
    }
} catch (InterruptException e) {
    Thread.currentThread().interrupt();
}

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

0 голосов
/ 24 февраля 2009

Как насчет планировщика на основе Quartz-Spring, который продолжает выполнять работу снова и снова с повторяющимися интервалами.

0 голосов
/ 24 февраля 2009

Это связано с колебаниями , но идея остается прежней: попробуйте использовать шаблон Observer вместо wile(true).

0 голосов
/ 24 февраля 2009

Похоже, ваша тема занята ожиданием. То есть он находится в цикле, в котором он ничего не делает. В такой ситуации он будет жевать много циклов процессора безрезультатно.

Как уже упоминалось, Thread.sleep - это ответ.

...