Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreading in a networked swing game: using invokeLater vs locks

I am writing a simple top down space game, and am extending it to allow play over a network with multiple players. I've done a fair bit of reading, but this is the first time I've done this and I'd appreciate some advice on choosing a sensible design.

My GUI is written using Swing. 30 times a second, a timer fires, and repaints my GUI according to data in a gameWorld object in memory (essentially a list of ships & projectiles with positions, etc). Physics updates of the gameWorld are also carried out using this timer. Thus, for the single player implementation, everything happens on the EDT, and this works fine.

Now, I have separate thread dealing with incoming packets from other players. I would like to update the data in my gameWorld object based on what these packets contain. My question is, should I use invokeLater to make these changes, or should I use locks to avoid concurrency problems?

To illustrate what I mean:

runMethodOfInputThread() {
    while(takingInput) {
        data = receiveAndInterpretIncomingPacket();  // blocks
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                gameWorld.updateWithNewGameInfo(data);
            }
        });
    }
}

vs

runMethodOfInputThread() {
    while(takingInput) {
        data = receiveAndInterpretIncomingPacket();  // blocks
        synchronize (gameWorldLock) {
            gameWorld.updateWithNewGameInfo(data);
        }
    }
}

The latter would also require using similar synchronize blocks wherever the EDT accesses the gameWorld, so it seems to me that using invokeLater would be simpler to implement. But am I right in thinking both approaches would work? Are there any other significant pros/cons to bear in mind?

Thanks,

Jeremy

like image 217
Jeremy Law Avatar asked Aug 17 '11 12:08

Jeremy Law


3 Answers

Well, first of all you don not need to choose only one method. You can use locks to make you data structure thread-safe "just to be sure" (since your application is already multithreaded), and use invokeLater to actually apply changes only in EDT -- and in this case JIT likely to optimize you locks down, close to 0.

Next, from my point of view invokeLater is rather preferred way: if you can way around dealing with multi-threaded -- you should use the way, just because multithreading is hard and rich of possible errors.

But applying changes via invokeLater() will put additional pressure on EDT, so, if changes come with high rate you can observe GUI degradation. Also, if gameWorld.updateWithNewGameInfo(data) is havy method taking observable time to complete, it can makes you GUI even freeze. Also, invokeLater puts your task at the tail of event queue, so it'll be done after all events currently in queue. It may -- in some cases -- cause delays in applying changes, which can makes you game less user-friendly. It may, or may not be your case, but you'll should keep it in mind

As for general rule -- not use EDT for any time consuming task. As far, as I understand, network packet parsing is already in seperate thread in your application. Applying changes can (and should) be done in separate thread too, if it is time consuming.

like image 73
BegemoT Avatar answered Nov 18 '22 04:11

BegemoT


Pros for approach 1:

  • Minimized complexity
  • Stability

By restricting access to the 'gameWorld' variable to the EDT thread, locking mechanisms are not required. Concurrent programming is complex and requires the programmer(s) to be vigilant throughout the source base when accessing objects shared amongst threads. It is possible for a programmer to forget to synchronize in certain instances, leading to compromised game states or program failure.

Pros for approach 2:

  • Scalability
  • Performance

Minimizing the processing done on the EDT thread ensures that the games interface and display will remain responsive to the user. Approach 1 may work for now, but later revisions of your game will not be able to scale to a more advanced interface if the EDT thread is busy doing non-ui processing.

like image 41
Brendan Cashman Avatar answered Nov 18 '22 04:11

Brendan Cashman


Not the second one. You want to have as little as possible running in the EDT. If you are waiting for a lock in the EDT, it's as bad as running all the other code (on the other side of the lock) directly in the EDT since the EDT has to wait for everything else to finish.

Also, it seems that your whole game is running on the EDT. That's bad practice. You should split your code using the model-view-controller pattern. I understand your game is small and can run in the EDT, but you should probably not get into the habit.

You should have your game logic running from a timer thread (java.util.concurrent.ScheduledThreadPoolExecutor) and at the end of every period you "send" your data to the EDT and repaint with invokeLater.

You should also have some separate thread that reads the socket and that thread should write to objects that share locks with the objects you are using in the timer game thread.

like image 1
toto2 Avatar answered Nov 18 '22 04:11

toto2