2

着信パケットをリッスンするために 4 つのスレッドを開始するアプリケーションがあります。すべてのスレッドは、異なるポートでソケットを開きます。通常、パケットは一度に 1 つのポートだけで受信されますが、場合によっては、メッセージが 2 つのポートで数秒間受信されることがあります。これらのスレッドのそれぞれがメッセージを処理し、一連のリスナーを更新します (それらはすべて Swing の描画処理を行っています)。メッセージは 10 Hz の頻度で送信され、Swing コンポーネントのペイント アクションには時間がかかるため、最初のアプローチは 20 のメッセージのうち 1 つだけを処理することでした (コンポーネントのペイントを完了するのに 2 秒かかります)。うまく機能します...

しかし、一度に 2 つのメッセージを受信した場合、そのうちの 1 つ (短時間だけ受信したもの) だけを処理するようにアプリケーションに指示する必要があります。要約すると、10 メッセージが 2 番目のポートで受信され、周波​​数も 10 Hz です。つまり、最初のアプローチを使用すると、20 のうち 1 つしか処理されないため、10 個すべてを見逃すことがあります。

2番目のポートでメッセージが受信されるたびに、アプリケーションでそのメッセージを処理したいのですが、1番目のポートで何を受信したか、その時点で何かが描画されているかどうかは関係ありません。

次のコードは、私のスレッドの実装を示しています。これらのうちの 4 つは、コンストラクターを介して指定されたポートに応じて同時に開始されます。

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 を更新する必要があるためです。Everyは異なる でメソッドをupdated呼び出します。したがって、それらはすべて一緒に実行する必要があります。paintComponent()JPanel

更新 2: 重要なメッセージ (2 番目のポートで受信) が失われる可能性があるため、最初のアプローチは使用できません。私が必要とするのは、通常のメッセージが受信されたときに、それを処理して描画を行うだけで、(1 番目のポートで) 新しい通常のメッセージがいくつ入っても関係ありません。しかし、2 番目のポートで 1 つのメッセージしか受信されない場合でも、アプリケーション通常の受信スレッドで何が起こっているかに関係なく、それを処理する必要があります。

ここで2つの問題に直面していると思います:

  1. 描画が完了するまで各スレッドを待機させる必要があります。これは UDP であるため、描画アクション中に通常のパケットを処理し、後続のすべての通常のパケットを忘れることができます。完了したら、次の通常のパケットを処理します。

  2. 2 番目のポートでパケットを受信した場合は、通常のパケット処理アクションをすべて中断し、特別なパケットの処理に必要な処理を行います。

問題 (1) は、MainIncomingClass の BitSet を使用して解決されます。すべてのリスナーは、ある種のコールバック関数を使用して、ペイントが完了したことを示し、BitSet に特定のビットを設定します。すべてが真ではない場合、新しいパケットは処理せず、手放します。

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 に答える