Есть ли разница между горутинами и await в C#?
Несмотря на то, что эти инструменты созданы для решения одних и тех же проблем, делают они это по-разному, как под капотом, так и в плане предлагаемых абстракций. Потому нельзя сказать что async/await в C# - это "просто обертка над горутинами".
Горутины в Go - это концепция stackful coroutines (под капотом) + CSP (в абстракциях). Каждый раз, когда мы создаём горутину, под неё выделяется отдельный стек вызовов для её собственных нужд. При этом, когда происходит паника, то stack unwinding (размотка стека вызовов) происходит только в пределах этой горутины и не покидает границ её стека. Стек горутины полностью отвязан от стека её создания/вызова, потому горутина не может возвращать результат. Любое общение между горутинами выполняется либо посредством каналов, либо какой-то общей памяти.
async/await в C# (то есть, Task'и) - это концепция stackless coroutines (под капотом) + futures (в абстракциях). Код с async/await'ами компилятор превращает в определенную стэйт-машину с yield point'ами. У них нет отдельного стека, они выполняются в том же стеке что и вызывающий их код. Соотвественно, есть возможность словить exception'ы (аналог panic'и) возникающие внутри асинхронного Task'а прямо в запускающем его коде. Так как выполнение идёт на том же стеке - Task нормально может возвращать результат и мы его можем считать в вызывающем коде без дополнительных примитивов/инструментов.
При этом, если мы запустим Go с GOMAXPROCS=1, то мы получим однопоточный асинхронный код в Go (по умолчанию он многопоточный). Также и в C# мы можем выполнять Task'и как на одном потоке, так и на thread pool, получая аналогичные Go гарантии рантайма.
С точки зрения абстракций/использования - это уже вкусовщина. Кому как больше нравится. У futures лучше дизайн в плане composability (их эргономичнее join'ить и select'ить), но они вынуждают писать везде async и await. У горутин надо постоянно городить чехарду с синхронизацией (попробуйте сделать аналог await для произвольной горутины), но если эту чехарду прятать под капот (как обычно и делают), то код вообще выглядит полностью синхронным и программисты радуются.
После вызова горутины выполнение немедленно перейдет к следующей строке, не дожидаясь, пока вызываемая функция завершится.  Ключевое слово await указывает на то что нужно ждать пока вызываемая функция вернет результат и только потом переходить к выполнению следующей строки.