It depends if you're describing a semantic model or you're concerned about implementation details.
Semantically, a goroutine is a thread, within a shared memory model. But what makes Go unique (or let's say more unique) is that it offers programmers a thread-like programming approach (linear, blocking code) but internally turns it into an event-driven approach (epoll/kqueue) for networking.
Moreover, the fact that goroutines are much cheaper than OS-level threads enable a more pervasive approach to concurrency.
Go uses an m:n thread model. Goroutines are multiplexed onto a smaller number of os-level threads. They're sort of like threads, but they have a simplified programming model (There is no thread-level storage for example).
For my information, why is M:N so successful for Go and not for pthreads? Is M:N more practical for systems with a particular kind of garbage collector?
In my opinion, it's because the Go team put a ton of effort into getting M:N working and didn't rigorously evaluate any other alternatives in Go, once moving GC was implemented.
I'm not convinced that 1:1 wouldn't have been a perfectly reasonable implementation strategy for Go.
This is surprising. It seems like Go gets pretty high praise from all over for its threading model, and it seems like there are relatively few high-performance servers that are built with 1:1. Am I wrong about that? If not, what explains this?
Semantically, a goroutine is a thread, within a shared memory model. But what makes Go unique (or let's say more unique) is that it offers programmers a thread-like programming approach (linear, blocking code) but internally turns it into an event-driven approach (epoll/kqueue) for networking.
Moreover, the fact that goroutines are much cheaper than OS-level threads enable a more pervasive approach to concurrency.