Search code examples
c#goasynchronousasync-awaitgoroutine

C# Task async/await vs Golang's goroutines


I have been learning both C# and Golang for a while and trying to compare the ways they support asynchronous programming.

What I know about goroutines is that the go runtime can schedule the goroutines to run on different physical/machine threads. If a goroutine is blocked (e.g., synchronous waiting for I/O), the go runtime can suspend the goroutine and free the physical threads to run other goroutines.

C# Task is similar to goroutine in the sense that it is also an abstraction on top of physical threads. However, it is considered a bad idea to do blocking I/O inside an async Task because "the entire thread will be blocked, causing deadlock". Can't the C# runtime do something similar to goroutines to suspend the blocked async Task and free the physical threads to run other async Tasks?

I have been struggling for this problem for a while and couldn't find public materials explaining this better. Maybe my understanding is not correct. Can someone please help me?


Solution

  • C# Task is similar to goroutine in the sense that it is also an abstraction on top of physical threads.

    At an extremely high view, yes, they can be similar.

    However, it is considered a bad idea to do blocking I/O inside an async Task because "the entire thread will be blocked, causing deadlock".

    Blocking doesn't necessarily cause deadlocks, but blocking will block the calling thread until the task completes. That's the whole point.

    Can't the C# runtime do something similar to goroutines to suspend the blocked async Task and free the physical threads to run other async Tasks?

    An async method can just use await to get the behavior you want. Blocking is specifically for blocking threads. Conceptually, goroutines are sort of like making every method async and implicitly using await everywhere.

    In the more general case of the C# runtime doing something similar to goroutines for any blocking: the C# runtime cannot easily do this because of backwards compatibility. There's also a lot of legacy code that depends on "special threads" (UI threads, COM STA threads, etc) where a green threading / coroutine approach would be more difficult. Go has mostly avoided backwards incompatibility concerns by just creating an entirely new ecosystem.