Tuesday, August 07, 2007

Threads - Don't Do It!

Here's a warning to anyone who's thinking of using threads - don't bother. Even if you've used them before, I think it serves well to be reminded (me included) that they should be avoided at all costs. Why? Because they make things exponentially more complicated!

Programming is complicated enough, trying to write a block of code that has zero bugs and defects. But what about if the variables you are using could change in mid-line? Or objects that you thought existed because you checked in the previous line now don't exist in the next line?

My multi-player games use at least two running threads; one is the main game loop, and another is the thread that waits for connections from players. This can cause problems in itself - the main game loop obviously loops through the players collection to send them data (and other stuff), but a player can join the game regardless of what stage the main game loop is at. So problems exist in that the main game loop could send all the players the current (say) ammo levels, but if a player had only just joined, they hadn't even been told that a particular unit exists in order to have a gun to contain the ammo!

Unfortunately, the keyboard input is also a seperate thread (as is the norm with Java), and until I corrected it, I was performing IO operations inside the KeyPressed() method. So the client software code could be halfway through sending the unit's location, when the player decide to press space to shoot, and suddenly a stream of bytes representing the "Shoot" command comes steaming through, completely confusing the server. The synchronised keyword fixed this problem of course.

This is just a few minor examples of threading problems. The irony is, that on the face of it threads seem like a great idea - like lots of little programs all doing their own thing. Unfortunately, it's when they have to interact with each other, which they always have to at some time, that problems start.

2 comments:

Charlie said...

I would be wary of throwing out threads as a tool. You just have to learn a few tricks for doing it right.

For instance, one Java project I work on implements a scheduler and messages in anonymous classes.

abstract Message { public void run(); }

KeyPress() {
// static scheduling
Scheduler.add(new Message() {
public void run() {
// code
}
}
}

This means that key events are dealt with when resources are available, and repeats can be avoided with some simple logic to prevent message buildup by key events. This keeps everything in a single thread but it's a powerful mechanism for cross-thread messaging?

Steve said...

That's one good way round it - passing messages between threads rather than interfering with them directly.

I did change my OnKeyPress() code to do something similar to that: basically, I created a boolean[255] which stores a flag for each key to say if the key had been pressed (one flag for each ascii code). Then the main game loop could check specific keys in it's own time.

i.e.

public void keyPressed(KeyEvent e) {
keys[e.getKeyCode()] = true;
}

public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}