the main thread, joining, and detaching
Every program starts with one thread already running - the main thread, the flow of control that begins in main(). When that program creates more threads, the main thread becomes a sort of coordinator, and a practical question arises: when a worker thread finishes, who cleans up after it, and does anyone wait for its result? The two answers are joining and detaching, and every thread you create should end up handled by one or the other.
Joining means waiting. When you call pthread_join() on a thread, your thread blocks until that thread has finished, and then collects its return value and lets the system reclaim its resources. It is how you say 'I need this worker to be done, and I want its result, before I move on'. Detaching means letting go. When you call pthread_detach() on a thread (or create it detached), you declare that you will never join it; the system will clean up its resources automatically the moment it finishes, and you neither wait for it nor get its return value. Use detach for fire-and-forget background work whose result you do not need. The pairing matters: a joinable thread that is neither joined nor detached leaks its bookkeeping resources when it ends - a small but real resource leak, the threading analogue of a zombie process.
Why this also matters at the program's end: when the main thread returns from main() (or any thread calls exit()), the whole process ends immediately, abruptly killing every other thread, finished or not. So if the main thread spawns workers and then falls off the end of main() without joining them, those workers may be cut off mid-task with their work half-done and never cleaned up. The usual discipline is: the main thread creates the workers, joins each one it cares about before exiting, and only then returns - guaranteeing the workers actually finished. (A thread that should outlive everything, like a background logger, is typically detached instead.)
A program splits a sum across four worker threads, then calls pthread_join() on all four. Only after the fourth join returns - so all four have certainly finished and stored their partial sums - does the main thread add the parts together and print the total.
Join to wait for results; detach for fire-and-forget. Returning from main() kills all threads at once.
A thread must be EITHER joined OR detached, never both and never neither - joining a detached thread is an error, and forgetting both leaks resources. Also, you cannot join a thread twice. Decide each thread's fate up front.