Проблема с потоками - PullRequest
2 голосов
/ 25 февраля 2009

* Я использую Java.

У меня есть этот поток, агент, который исследует комнату, чтобы определить ее размер и очистить ее, если она грязная. Тогда у меня есть интерфейс, который рисует агент, поскольку он исследует окружающую среду. Агент является подклассом из Thread, а Java заботится об управлении потоками. Все, что я делаю, это создаю поток и говорю object.start ().

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

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

...
public void ActionPerformed(ActionEvent e)
{
    //disable the menus with setEnabled(false);
    agent.start();
    //enable the menus with setEnabled(true);
}

Проблема в том, что меню активируются до того, как поток агента выполнит свою функцию. Я думал об использовании Thread.join () - это гарантировало бы, что код для включения меню выполняется только после завершения потока агента. Но, если я использую Thread.join (), интерфейс не обновляется, пока агент движется, потому что он ждет завершения агента!

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

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

Ответы [ 4 ]

5 голосов
/ 25 февраля 2009

Я думал об отключении интерфейс от агента, а затем включить его, как только агент будет сделан, но я не уверен, что это сработает и самая большая проблема здесь в том, что агент не должен возиться с меню.

Вы можете заставить агента вызывать метод в классе GUI, который снова включает меню, когда это будет сделано. Этот метод, конечно, будет вызывать соответствующие методы Swing для EDT.

class Agent extends Thread
{
    @override
    public void run()
    {
       // run around the room

       // finally done
       gui.agentIsDone();
    }
}

class GUI extends JFrame
{
    ...

    void agentIsDone()
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @override 
            public void run()
            {
                menus.setEnabled(true);
            }
        });
    } 
}

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

2 голосов
/ 25 февраля 2009

Похоже, вам нужен агент для выдачи уведомления (кто-нибудь еще использует Observer / Observable?) Для вашего интерфейса, что очистка началась и завершена. Ваш интерфейс, соответственно, будет вызывать setEnabled(false) и setEnabled(true) при получении этих уведомлений.

РЕДАКТИРОВАТЬ: Идея состоит в том, чтобы отделить манипуляции с меню от выполнения агента. То есть что произойдет, если вы введете другой способ инициировать очистку, кроме пункта меню? Вы, вероятно, все еще хотите, чтобы меню в этом случае было отключено.

1 голос
/ 25 февраля 2009

В вашем методе actionPerformed было бы хорошо просто отключить меню и затем запустить поток, но вам не следует снова включать меню в той же функции. Скорее, вам нужно будет активировать меню, когда закончится поток. Я бы посоветовал в конце цепочки вашего агента вызвать SwingUtilities.invokeLater() с Runnable, который включит меню.

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

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

Кроме того, предположительно, это не очень хорошая практика - создавать подклассы Thread. Вместо этого вы должны реализовать Runnable, а затем создать поток с вашим Runnable в качестве цели и запустить поток. Таким образом, если ваш агент является Runnable, вы можете запустить его в своем собственном потоке или в основном потоке (просто вызвав run ()) или в пуле потоков и т. Д.

...