Are goroutines appropriate for large, parallel, compute-bound problems?

The Go runtime has a model where multiple Go routines are mapped onto multiple threads in an automatic fashion. No Go routine is bound to a certain thread, the scheduler may (and will) schedule Go routines to the next available thread. The number of threads a Go program uses is taken from the GOMAXPROCS environment variable and can be overriden with runtime.GOMAXPROCS(). This is a simplified description which is sufficient for understanding.

Go routines may yield in the following cases:

  • On any operation that might block, i.e. any operation that cannot return a result on the spot because it is either a (possible) blocking system-call like io.Read() or an operation that might require waiting for other Go routines, like acquiring a mutex or sending to or receiving from a channel
  • On various runtime operations
  • On function call if the scheduler detects that the preempted Go routine took a lot of CPU time (this is new in Go 1.2)
  • On call to runtime.Gosched()
  • On panic()
  • As of Go 1.14, tight loops can be preempted by the runtime. As a result, loops without function calls no longer potentially deadlock the scheduler or significantly delay garbage collection. This is not supported on all platforms - be sure to review the release notes. Also see issue #36365 for future plans in this area.
  • On various other occasions

The following things prevent a Go routine from yielding:

  • Executing C code. A Go routine can't yield while it's executing C code via cgo.
  • Calling runtime.LockOSThread(), until runtime.UnlockOSThread() has been called.