Like C#, core.async uses a lexical compiler transform to produce a finite state machine for the co-routine. Unlike C#, Clojure can achieve this with a user-level macro, instead of a compiler change. Both C# and core.async differ from Go, in that Go's coroutines have dynamic extend, by virtue of being heap-allocated stacks with a custom scheduler. In practice, this has a minor impact on higher-order usage of co-routines, but is a smaller problem than you'd think, it's generally advisable to minimize higher order usage of side effects.
Both C# and Go's approaches can be implemented as Monads, yes. However, Monads are a significantly more abstract thing than either CSP or C#-style Tasks. The do-notation is barely concealed continuation-passing style, which is generally less pleasant to work with than traditional imperative constructs for side effects such as send & receive. "More powerful" isn't a really useful measurement for practical use.