It is cooperative multitasking indeed.
What about a small program to prove it. Let's first sleep with cooperative asyncio.sleep for a second and then let's sleep with blocking time.sleep for a second. Let's print a thread id, time spent in the coroutine and id of a task.
import threading
import asyncio
import time
async def async_function(i):
    started = time.time()
    print("Id:", i, "ThreadId:", threading.get_ident())
    await asyncio.sleep(1)
    time.sleep(1)
    print("Id:", i, "ThreadId:", threading.get_ident(), "Time:", time.time() - started)
async def async_main():
    await asyncio.gather(
        async_function(1),
        async_function(2),
        async_function(3)
    )
loop = asyncio.get_event_loop()
loop.run_until_complete(async_main())
Now let's try and see:
Id: 3 ThreadId: 140027884312320
Id: 2 ThreadId: 140027884312320
Id: 1 ThreadId: 140027884312320
Id: 3 ThreadId: 140027884312320 Time: 2.002575397491455
Id: 2 ThreadId: 140027884312320 Time: 3.0038201808929443
Id: 1 ThreadId: 140027884312320 Time: 4.00504469871521
As expected. Execution was only in one thread. asyncio.sleep(1) is nonblocking, so it took 1 second to process all of them concurrently. time.sleep(1) is blocking (it does not cooperate), so it blocks the rest. Id 1 waits for id 2 to finish while id 2 waits for id 3 to finish.
C# has async/await too, does it have cooperative multitasking as well?
Let's try the same thing in C#:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncTest
{
    class MainClass {
        private static async Task AsyncMethod(int id) {
            var started = DateTime.Now;
            Console.WriteLine("Id: {0} ThreadId: {1}", id, Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(1000);
            Thread.Sleep(1000);
            Console.WriteLine("Id: {0} ThreadId: {1} Time: {2}", id, Thread.CurrentThread.ManagedThreadId, DateTime.Now - started);
        }
        private static async Task MainAsync()
        {
            await Task.WhenAll(AsyncMethod(1), AsyncMethod(2), AsyncMethod(3));
        }
        public static void Main (string[] args) {
            MainAsync().Wait();
        }
    }
}
Run it and...
Id: 1 ThreadId: 1
Id: 2 ThreadId: 1
Id: 3 ThreadId: 1
Id: 2 ThreadId: 7 Time: 00:00:02.0147000
Id: 3 ThreadId: 8 Time: 00:00:02.0144560
Id: 1 ThreadId: 6 Time: 00:00:02.0878160
Damn. The threads are different after await. And it tooks just 2 seconds for each of the coroutine! What's wrong?
Nothing is wrong. Unlike Python, async/await in C# has a combination of cooperative multitasking and multithreading. Task.Delay(1000) is indeed nonblocking but when a coroutine resumes, it can resume in a totally different thread as it did in the example. Since the coroutines continued in three different threads, Thread.Sleep(1000) blocked them in parallel.
Note there are more things in C# which can influence this behavior (like SynchronizationContext), but this is a different topic.