Coroutines may not be a familiar concept to programmers of C/C++ and Java. Python programmers may compare them to generators, although slightly different ( coroutines are more powerful) they are often used in very similar ways.
Most modern operating systems support preemptive multitasking, i.e. each job (appears to) run in parallel and the operating system is responsible for the cpu scheduling. A job can be interrupted at any moment and it will not notice it and it doesn’t have to care about it.
Cooperative multitasking is a less sophisticated approach where each job, just like in preemptive multitasking is executed for a while, then paused in favor of another one. But in this case, no job will be interrupted unless it actually shouts “Hey! This is a good time for switching to another job”. The operation is more formally called “yielding”. If you add cooperative multitasking to a programming language you end up with something very much like coroutines. If you instead add it as a library you get something like fibers.
A main disadvantage is that programs written using coroutines/fibers will not run any faster than programs written without them. This is all because the model of cooperative multitasking doesn’t use real threads and it doesn’t benefit from multiple cpu cores.
An advantage of supporting such a technique is that it doesn’t require much for the operating system, it doesn’t need support for threads and works on very primitive platforms such as (MS-)DOS.
Even on modern systems the yielding model is useful since a very convenient way of expressing state machines. It may also improve the overall design and separation of concerns is a larger program. When a program yields it can return and read values (as shown in my Lua example). In this case using coroutines is a much simpler alternatives compared to using threads and semaphores, mutexes or message queues.