David Beazley does a much better illustrating the nuances than I ever could:
https://www.youtube.com/watch?v=Obt-vMVdM8s
Including a wild phenomenon where a threaded Python program's performance can degrade by increasing number of CPU cores available to a system.
Coroutines avoid this by not requiring individual thread scheduling by the OS. So they effectively release the GIL like threads on IO but avoid OS level thread switching.