What you
should do is to signal the thread in some way that it should now please terminate
itself.
For example, set a global boolean that says to the thread-world, "okay, folks, game over." Then, signal all the condition-variables that the respective threads are waiting on. They should all, in their turn, notice that the game-over flag is set, and proceed in an orderly fashion to put their houses in order and die.
If your threads naturally do things that might "run too long," then you should
already have some kind of signal-mechanism in place that can be used to asynchronously notify them of the referee's-whistle. (Presumably you would use this for any sort of request. Such a signal, by itself, does
not "cause the thread to die.") So you might do all three things:
- First, set the game-over flag.
- Then, signal everyone's condition-variables so if they're sleeping now they'll wake up.
- Finally, if necessary, send a user-signal to the threads in case they might be busy, but be sure that the threads know to ignore that signal (or at least do-nothing) when they are in the process of closing down. The signal must be "absorbed," quietly.
The strategy here is that threads handle their termination in exactly the same fashion that they handle any other sort of request: it's not handled like any sort of exception. The thread simply stops accepting new requests, releases its resources, and dies.
"Termination time" is always the second-bumpiest moment in a program's life. There's a great potential for timing-holes at that point, and your design here must be careful and thorough.