You SHOULD NOT use threads.

Some people would have you believe that NULL is the worst mistake in computer science. They make a good case for it, but they're dead wrong. Multithreading is the worst mistake in the history of computers. Ask anyone who has debugged both a segfault and a race condition. They will assure you that the latter is 10-100× more difficult to solve.

Look at your program's CPU usage. If the sum of your threads is <100% of one core, then there is no reason whatsoever for you to use threads. If your program is not CPU bound, then you would be infinitely wiser to use an event loop1 or coroutines2. If you are not CPU bound, then the entire value proposition of threads does not apply to you.

A thread has access to the same address space as other threads in your process, and can do whatever they want with it. You have to take extreme pains to avoid accidentally doing the wrong thing. You'll probably mess it up, and the symptoms will show up in an unrelated part of the program 10 minutes later. There is no shame in making such mistakes — we're only human, after all. The shame is in believing ourselves super-human, and reaching for a tool that we don't need, in full knowledge that we're likely to shoot ourselves in the foot with it. Nine out of ten times, this tool is threads. If you're asked to dig a hole and you point a pistol between your feet to get the job done... you should have used a shovel.

But, perhaps your problem is CPU bound. In that case — you still shouldn't use threads! Consider using multiple processes instead. A good model is an overseer program, which organizes the work to be done and aggregates the results, spawning worker programs to run the actual computations. This is not only much more robust, but it scales better, since you could distribute these programs across multiple computers later on. This approach is more useful, too, because often the user of the program may have a more novel idea about how to distribute work, and if they can invoke the worker processes themselves, they're empowered to try it out4. This is easier to test and debug for the same reasons. And, in addition to all of these benefits, you get a brand new address space which is much more difficult to fuck up.

But, maybe, you use a language which claims to guarantee the safety of multithreaded code. First of all, consult the list of advantages of multiprocessing provided given in the previous paragraph, which are useful even beyond the simple case of "is it safe". Second, your language designers are lying to you, or perhaps to themselves. On a long enough time-scale, all programs will be proven incorrect. All code has bugs, including your compiler and language runtime. The more complex the program, the more often it will break and the worse those failures will be. You might be able to point the blame at someone else, but ultimately it's your program that's going to be broken.

Don't use threads! It's a trap!

1 An event loop is where you have a single-threaded loop which processes events, which describes nearly all I/O-bound applications. Reading bytes from disk, receiving data from the network, waiting for user input — these are all events, and if you structure your program around (1) sleep until an event arrives; (2) process that event; (3) goto 1; you will have a very reliable program.

2 Coroutines are a common form of cooperative multitasking. Multithreading is a kind of preemptive multitasking3 — each thread is "preempted", and can be interrupted at any moment, to switch tasks. In a cooperative system, each task voluntarily cedes control to the next task. Coroutines are a kind of "best of both worlds" solution. You write blocking code normally, and when it would block on I/O (or on something else), the runtime switches to another task while your I/O operation completes.

3 "But on a multi-core system, threads aren't preempted, they run in parallel!" Not true. Run this command: grep processor /proc/cpuinfo | wc -l. This is the number of tasks your computer is capable of executing in parallel. Now run this command: ps aux | wc -l. This is the number of tasks your computer is currently working on. Q.E.D.

4 Hint: your "overseer program" can often be xargs -P$(nproc).