javaJake said:
Well, threading was never really about speed, but about structure. I don't know what you mean about nondeterminism, though, and would like to hear more about pitfalls to avoid.
I probably assumed a bit too much from the "so that each could receive the event at basically the same time and speed things up" part, never mind.
I haven't really written a lot of threaded applications, but here are some of the things I've encountered with the ones I wrote. I don't know, maybe I just suck at threads;
The pitfalls are mostly where you'd expect, except one tends to fall in them anyway.
Say you have two threads, one moving an object right every second, and one moving it up. You could easily get two up-movements or two right-movements in a row, depending on thread scheduling, making the path it takes partly random.
Or say your game loop consists of sending some messages, and rendering a frame. If you don't explicitly synchronize, there's no telling which of the messages have been processed and which haven't, so you could end up rendering a couple of frames without any messages getting processed, then half of them gets acted on, then a couple messages gets half done in the middle of rendering, etc. You might get the right result, if none of the actions over different threads are order dependent, but it could look a bit jerky. Similarly, if you want the threads to do some actions on shutting down, you have to synchronize them somehow, or you won't know when it's safe to delete stuff the threads may be using, etc.
When you use threading in C++, it's likely you'll be using locks of some sort for shared resources. Say you want to move messages between two queues, with semaphores A and B. If some code that gets called from one thread says CODE
A.wait(); B.wait(); ...; B.signal() A.signal()
and code in another thread says CODE
B.wait(); A.wait(); ...; A.signal() B.signal()
(except probably with try/finally / RAII), you have a heisenbug where very rarely, both threads will randomly get into a deadlock.
A lot of the complexity comes from shared data structures, so it's a good idea to avoid those as much as possible, which is why message passing is a good idea. If two threads try to modify a data structure like a linked list at the same time, the result could be gibberish, and then it'll crash some time later when you're accessing the list, and you haven't got a chance of reproducing it in a debugger. Anything mutable that several threads need access to should be protected by some sort of lock/mutex (preferably by making a thread-safe class).