2

我有一个应用程序启动四个线程来监听传入的数据包。每个线程在不同的端口上打开一个套接字。通常,数据包一次只能在一个端口上接收,但在某些情况下,消息可以在两个端口上接收几秒钟。这些线程中的每一个都处理消息并更新一堆听众(他们都在做一些 Swing 绘画的事情)。由于消息以 10 Hz 的频率发送,并且 Swing 组件上的绘制动作需要一些时间,我的第一种方法是只处理 20 条消息中的一条(完成组件上的绘制需要 2 秒时间)。效果很好...

但是当当时收到两条消息时,我需要告诉我的应用程序只处理其中一条(只在短时间内收到的一条)。总之,在第二个端口上接收到 10 条消息,频率也为 10 Hz。意思是,使用第一种方法有时我会错过所有 10 个,因为只有 20 个被处理。

每当收到第二个端口上的消息时,我希望我的应用程序处理那个消息,不管在第一个端口上接收到什么或当时是否绘制了某些东西。

以下代码显示了我的线程的实现,其中四个同时启动,具体取决于通过构造函数给出的端口。

private class IncomingRunner implements Runnable {

    private int listenPort;
    private DatagramSocket localSocket;
    private DatagramPacket packet;
    private int counter = 0;

    public IncomingRunner(int port) {
        this.listenPort = port;
    }

    @Override
    public void run() {
        try {
            localSocket = new DatagramSocket(listenPort);

            byte[] buffer = new byte[1024];
            packet = new DatagramPacket(buffer, buffer.length);

            while(isRunning)
                recvIncomingMsg();

        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    private void recvIncomingMsg() {
        try {
            localSocket.receive(packet);
            port = localSocket.getLocalPort();

            ReceivedMsg eventMsg;

            if(port == Config.PORT_1) {
                eventMsg = new ReceivedMsg(Config.PORT_1, Config.SOMETHING_1);
                System.out.println(HexWriter.getHex(packet.getData()));
            } else if (port == Config.PORT_2) {
                eventMsg = new ReceivedMsg(Config.PORT_2, Config.SOMETHING_2);
                System.out.println(HexWriter.getHex(packet.getData()));
            } else if (port == Config.PORT_3) {
                eventMsg = new ReceivedMsg(Config.PORT_3, Config.SOMETHING_3);
                System.out.println(HexWriter.getHex(packet.getData()));
            } else {
                eventMsg = new ReceivedMsg(Config.PORT_4, Config.SOMETHING_4);
                System.out.println(HexWriter.getHex(packet.getData()));
            }

            counter++;

            if(counter%20 == 0) {
                forward2PacketPanel(eventMsg);
                counter = 0;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void forward2PacketPanel(final ReceivedMsg t) {
        for(final IPacketListener c : listeners) {
            if(c instanceof IPacketListener) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ((IPacketListener)c).update(t);
                    }
                }).start();
            }
        }
    }
}

更新: 我开始新线程来更新监听器的原因是,因为它们都应该同时更新 GUI。每个updated调用paintComponent()不同的方法JPanel。所以他们应该一起运行。

UPDATE2: 我不能使用第一种方法,因为这会导致消息丢失可能是重要的消息(在第二个端口接收)。我需要的是,当收到正常的消息时,只需处理它并进行绘制,不管有多少新的正常消息(在第一个端口上)进来。但即使在第二个端口上只收到一个消息,应用程序需要处理那个,不管正常接收线程中发生了什么。

我想我在这里面临两个问题:

  1. 我需要让每个线程等到绘画完成,因为那是 UDP 我可以处理一个正常的数据包,而在绘画动作期间忘记所有后续的正常数据包。完成后,处理下一个正常数据包。

  2. 如果接收到第二个端口上的数据包,则中断所有正常的数据包处理操作并执行处理特殊数据包所需的事情。

使用 MainIncomingClass 中的 BitSet 解决了问题 (1)。每个 Listener 都使用某种回调函数来指示它已完成绘制并在 BitSet 中设置一个特定的 Bit。如果不是所有的都是真的,我不会处理任何新的数据包,只是让他们走。

4

1 回答 1

2

他们在这里谈论事件调度线程。您必须使用它来更新您的 GUI。幸运的是,您还可以使用它以您想要的任何顺序发布您的更新。EDT 会start()为您解决问题。您仍然需要同步对t.

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        ((IPacketListener)c).update(t);
    }
});
于 2012-10-15T13:38:03.343 回答