I hope that last example showed you some of the complexity of using threads, even for comparatively simple tasks. Threads are great for some things. If you want to do a bit of work in the background that doesn’t affect the global state, then they’re ideal. Once you start getting a significant amount of communication between threads, however, you’ll find the complexity of your program growing exponentially.
Threads offer a few advantages over processes for parallel programming. They tend to be cheaper to create and destroy, and they have higher-level synchronization mechanisms available to them. The shared address space makes it easier to communicate large amounts of data between threads, as this can be done simply by copying a pointer.
On the downside, pthread support is quite variable across operating systems. Threads also make code much more difficult to debug. A dangling pointer in one thread can overwrite data that will later cause a completely unrelated thread to crash.
Like many other tools, threads can be very handy when used carefully. Used inappropriately, however, they can cause serious headaches.