I've found that it is often impossible to debug multi-threaded code in a debugger. Hitting a breakpoint and single-stepping through code on one thread smashes any other reads that are operating and expecting the thread that is stopped to respond in a reasonable timeline. It can also cover up race conditions, and just generally dork up anything that is time-related.
If you have a race condition in a multi threaded program, a simple printf is enough to screw the timings and make the bug disappear, it's not at all better than breakpoints and stepping in a debugger.
In my experience, the only way to produce safe multi threaded code is to isolate the threading parts and synchronization and stress test them early on. At this point I use a combination of prints, random delays and simple asserts on invariants that the code should hold. Most issues are reproduced within about 10 seconds of 100% CPU core utilization. Some nasty corner cases may require minutes of grinding away.
Multi threaded debugging is an unsolved problem. There are no universal solutions to this problem. Conventional tools often fail so badly that it is best to write and test your multi threading / synchronization code in isolation, and then applying it to the actual workload which is tested in a single thread.
Another option is trying to build a formally verified model ahead of time (e.g. using Spin - www.spinroot.com) and then write the actual program after the model has been verified.
> If you have a race condition in a multi threaded program, a simple printf is enough to screw the timings and make the bug disappear, it's not at all better than breakpoints and stepping in a debugger.
If you have a race condition in a multi threaded program, a simple printf may be enough to change the timings and make the bug disappear. But a breakpoint and stepping in a debugger is guaranteed to completely change the timings (unless your timings are on the order of minutes).
In concurrent software development, the third way (analysis) is the only way. You need to know (or find out) which resources can be accessed concurrently, and develop a theory of how this could lead to the observed error.
Printf debugging forever! ;-)