Как работает stream отправки событий?
С помощью людей в stackoverflow мне удалось получить следующий рабочий код простого обратного отсчета GUI (который просто отображает окно, отсчитывающее секунды). Моя основная проблема с этим кодом – это материал invokeLater
.
Насколько я понимаю invokeLater
, он отправляет задачу в stream диспетчеризации событий (EDT), а затем EDT выполняет эту задачу всякий раз, когда она «может» (что бы это ни значило). Это правильно?
Насколько я понимаю, код работает следующим образом:
- Что такое WPF Preview Events?
- Глобальные события в угловых
- Порядок событий «Form.Load», «Form.Shown» и «Form.Activated» в Windows Forms
- Получить значения DataKey в GridView RowCommand
- Как добавить событие в UserControl в C #?
-
В
main
методе мы используемinvokeLater
для отображения windows (методshowGUI
). Другими словами, код, отображающий окно, будет выполнен в EDT. -
В
main
методе мы также запускаемcounter
а счетчик (по конструкции) выполняется в другом streamе (так что это не в случае диспетчерского streamа). Правильно? -
counter
выполняется в отдельном streamе и периодически вызываетupdateGUI
.updateGUI
должен обновить GUI. И графический интерфейс работает в EDT. Таким образом,updateGUI
также должен быть выполнен в EDT. Именно по этой причине код дляupdateGUI
заключен вinvokeLater
. Это правильно?
Мне непонятно, почему мы вызываем counter
из EDT. Во всяком случае, он не выполняется в EDT. Он запускается немедленно, там выполняется новый stream и counter
. Итак, почему мы не можем вызывать counter
в основном методе после блока invokeLater
?
import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class CountdownNew { static JLabel label; // Method which defines the appearance of the window. public static void showGUI() { JFrame frame = new JFrame("Simple Countdown"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); label = new JLabel("Some Text"); frame.add(label); frame.pack(); frame.setVisible(true); } // Define a new thread in which the countdown is counting down. public static Thread counter = new Thread() { public void run() { for (int i=10; i>0; i=i-1) { updateGUI(i,label); try {Thread.sleep(1000);} catch(InterruptedException e) {}; } } }; // A method which updates GUI (sets a new value of JLabel). private static void updateGUI(final int i, final JLabel label) { SwingUtilities.invokeLater( new Runnable() { public void run() { label.setText("You have " + i + " seconds."); } } ); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); } }
- Как изменить Form1 label.text при установке флажка на form2?
- ActionListener для конкретного текста внутри JTextArea?
- Как получить подписчиков мероприятия?
- Делегирование: EventEmitter или Наблюдаемое в Угловом
- Реализация сканера штрих-кода на Java
- Пользовательские события в jQuery?
- Обнаружение событий вставки / удаления USB в Windows с использованием C ++
- ga или _gaq.push для отслеживания событий Google Analytics?
Если я правильно понял ваш вопрос, вам интересно, почему вы не можете этого сделать:
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); } }); counter.start(); }
Причина, по которой вы не можете этого сделать, заключается в том, что планировщик не дает никаких гарантий … только потому, что вы вызывали showGUI()
а затем вы вызывали counter.start()
, не означает, что код в showGUI()
будет выполнен до код в методе запуска counter
.
Подумайте об этом так:
- invokeLater
запускает stream, и этот streamназначает асинхронное событие на EDT, которому поручается созданиеJLabel
. - счетчик – отдельный stream, который зависит от
JLabel
чтобы он мог вызватьlabel.setText("You have " + i + " seconds.");
Теперь у вас есть условие гонки: JLabel
должен быть создан до начала counter
streamа, если он не создан до начала встречного streamа, то ваш счетный stream будет вызывать setText
на неинициализированном объекте.
Чтобы убедиться, что условие гонки устранено, мы должны гарантировать порядок выполнения и один из способов гарантировать, что он должен выполнять showGUI()
и counter.start()
последовательно в одном и том же streamе:
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); }
Теперь showGUI();
и counter.start();
выполняются из одного streamа, поэтому JLabel
будет создан до запуска counter
.
Обновить:
Q: И я не понимаю, что особенного в этой теме.
A: Код обработки событий Swing запускается в специальном streamе, известном как stream отправки событий. В этом streamе также работает большинство кода, который вызывает методы Swing. Это необходимо, потому что большинство методов объекта Swing не являются «streamобезопасными»: вызывать их из нескольких streamов подвергает streamу помехи или ошибки согласованности памяти. 1Q: Итак, если у нас есть GUI, зачем нам запускать его в отдельном streamе?
Ответ: Вероятно, есть лучший ответ, чем мой, но если вы хотите обновить GUI из EDT (что вы делаете), то вам нужно запустить его из EDT.Q: И почему мы не можем просто начать stream, как любой другой stream?
A: См. Предыдущий ответ.В: Почему мы используем какой-то invokeLater и почему этот stream (EDT) начинает выполнять запрос, когда он готов. Почему он не всегда готов?
A: EDT может иметь некоторые другие события AWT, которые он должен обрабатывать.invokeLater
Заставляет doRun.run () выполняться асинхронно в streamе диспетчеризации событий AWT. Это произойдет после того, как все ожидающие события AWT будут обработаны. Этот метод следует использовать, когда stream приложений должен обновлять графический интерфейс. 2
Вы фактически запускаете counter
streamа из EDT. Если вы invokeLater
counter.start()
после блока invokeLater
, счетчик, скорее всего, начнет работать до того, как графический интерфейс станет видимым. Теперь, поскольку вы создаете графический интерфейс в EDT, GUI не будет существовать, когда counter
начнет его обновлять. К счастью, вы, похоже, перенаправляете обновления GUI в EDT, что верно, и поскольку EventQueue является очередью, первое обновление произойдет после создания графического интерфейса, поэтому не должно быть причин, почему это не сработает. Но в чем смысл обновления графического интерфейса, который еще не может быть видимым?
Что такое EDT?
Это хакерское решение вокруг множества проблем параллелизма, которые имеет Swing API;)
Серьезно, многие компоненты Swing не являются «streamобезопасными» (некоторые известные программисты дошли до вызова Swing «thread invile»). Имея уникальную цепочку, в которой все обновления добавляются к этим не зависящим от streamа компонентам, вы уклоняетесь от множества возможных проблем параллелизма. В дополнение к этому вы также гарантируете, что он должен запустить Runnable
который вы проходите через него, используя invokeLater
в последовательном порядке.
Затем некоторые nitpicking:
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); }
А потом:
В основном методе мы также запускаем счетчик, а счетчик (по конструкции) выполняется в другом streamе (так что это не в случае диспетчерского streamа). Правильно?
Вы действительно не запускаете счетчик в основном методе. Вы запускаете счетчик в методе run () анонимного Runnable
который выполняется в EDT. Таким образом, вы действительно запускаете счетчик Thread
из EDT, а не основной метод. Затем, поскольку это отдельный stream, он не запускается на EDT. Но счетчик определенно запускается на EDT, а не в Thread
выполняющем main(...)
метод.
Это ничтожный, но все же важный вопрос, который я думаю.
Это просто, это так
Шаг 1 . Создается начальная нить, также называемая основным streamом.
Шаг 2. Создайте исполняемый объект и передайте его invokeLate ().
Шаг 3. Это инициализирует GUI, но не создает графический интерфейс.
Шаг 4. InvokeLater () планирует созданный объект для выполнения на EDT.
Шаг 5. Создан GUI.
Шаг 6. Все происходящие события будут помещены в EDT.