Most of the above answers are related to performance and simultaneous operation. I will look at this from a different angle.
Let's take for example a simplified terminal emulation program. Here's what you need to do:
– Watch for incoming characters from the remote system and display them – Watch for data coming from the keyboard and send it to the remote system
(Real terminal emulators do more, including potentially echoing the data you type on the display, but we'll pass on this for now.)
Now the loop to read from the remote is simple according to the following pseudo code:
The loop to monitor the keyboard and send is also simple:
The problem, however, is that you need to do this at the same time. The code must look more like this now if you don't have threading:
The logic is quite obfuscated even in this deliberately simplified example, which does not take into account the real complexity of communication. With threading, however, the two pseudocode loops can exist independently on a single core without interleaving their logic. Since both threads are mostly E / A bound, they don't put a heavy load on the CPU, although strictly speaking they waste more CPU resources than the built-in loop does.
Now, of course, its use in the real world is more complicated than described above. However, the complexity of the built-in loop increases exponentially as you add more concerns to the application. Logic becomes more fragmented and you need to use techniques such as state machines, coroutines, etc. Use threading to make things manageable. Manageable, but not readable. Threading keeps the code more legible.
Why you should not use threading?
If your tasks are CPU-bound instead of I/O-bound, threading actually slows down your system. The service will suffer. In many cases much. ("Thrashing" Is a common problem when you delete too many CPU-bound threads. You spend more time changing the active threads than executing the content of the threads themselves.) One of the reasons the above logic is so simple is that I deliberately chose a simplified (and unrealistic) example. If you want to repeat the input on the screen, you have a new world of violations when you introduce shared resource locking. With only one shared resource, this isn't as much of a problem, but it gets bigger the more resources you can share.
So in the end, threading is about many things. For example, it's about making E / A bound processes more responsive (even if less efficient overall), as some have already said. It's also about following logic more easily (but only if you minimize shared status). There are a lot of things at stake, and you have to decide on a case-by-case basis whether the advantages outweigh the disadvantages.