Real threads can too expensive sometimes. A single coroutine in Go just use few kb of memory. So we can easily spawn hundred of thousands of coroutines but not possible using threads
Your point is correct but your magnitudes are off.
IIRC, a thread takes up ~32kB or so worth of data in pthreads/Linux, or at least... somewhere around there. So on a rather cheap server with 64GB of RAM, you can easily fit 1-million threads. More expensive servers have 512GB, 1TB or more RAM and can easily handle this kind of load.
Of course, coroutines are even smaller/more efficient and can go into 10-million, 100-million or higher.
A bigger issue is creating and destroying threads, which can be costly if you don’t do a lot of work per each. Of course, people have worked around this problem for ages with thread pools, but then you’re already breaking up ownership, code flow and screwing up the stack for debugging and panicking. So something like coroutines or green threads is still motivated, it is just extremely different across languages and is almost always a leaky abstraction.