Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Goroutines are cooperatively scheduled. Does that mean that goroutines that don't yield execution will cause goroutines to run one by one?

From: http://blog.nindalf.com/how-goroutines-work/

As the goroutines are scheduled cooperatively, a goroutine that loops continuously can starve other goroutines on the same thread.

Goroutines are cheap and do not cause the thread on which they are multiplexed to block if they are blocked on

  • network input
  • sleeping
  • channel operations or
  • blocking on primitives in the sync package.

So given the above, say that you have some code like this that does nothing but loop a random number of times and print the sum:

func sum(x int) {
  sum := 0
  for i := 0; i < x; i++ {
    sum += i
  }
  fmt.Println(sum)
}

if you use goroutines like

go sum(100)
go sum(200)
go sum(300)
go sum(400)

will the goroutines run one by one if you only have one thread?

like image 664
m0meni Avatar asked May 26 '16 20:05

m0meni


People also ask

Are Goroutines cooperative?

Scheduling of Goroutines. As I have mentioned in the last paragraph, Goroutines are cooperatively scheduled. In cooperative scheduling there is no concept of scheduler time slice.

What are Goroutines and how do they work?

GoRoutines are a Golang wrapper on top of threads and managed by Go runtime rather than the operating system. Go runtime has the responsibility to assign or withdraw memory resources from Goroutines. A Goroutine is much like a thread to accomplish multiple tasks, but consumes fewer resources than OS threads.

Are Goroutines multithreaded?

With Go, it's possible to do multi-threaded concurrency and parallelization with goroutines and goroutines work in an asynchronous way hence making use of both multi-threading and asynchronous programming efficiently.

How is Goroutine implemented?

Implementation. Goroutines are lightweight, costing little more than the allocation of stack space. The stacks start small and grow by allocating and freeing heap storage as required. Internally goroutines act like coroutines that are multiplexed among multiple operating system threads.


1 Answers

A compilation and tidying of all of creker's comments.

Preemptive means that kernel (runtime) allows threads to run for a specific amount of time and then yields execution to other threads without them doing or knowing anything. In OS kernels that's usually implemented using hardware interrupts. Process can't block entire OS. In cooperative multitasking thread have to explicitly yield execution to others. If it doesn't it could block whole process or even whole machine. That's how Go does it. It has some very specific points where goroutine can yield execution. But if goroutine just executes for {} then it will lock entire process.

However, the quote doesn't mention recent changes in the runtime. fmt.Println(sum) could cause other goroutines to be scheduled as newer runtimes will call scheduler on function calls.

If you don't have any function calls, just some math, then yes, goroutine will lock the thread until it exits or hits something that could yield execution to others. That's why for {} doesn't work in Go. Even worse, it will still lead to process hanging even if GOMAXPROCS > 1 because of how GC works, but in any case you shouldn't depend on that. It's good to understand that stuff but don't count on it. There is even a proposal to insert scheduler calls in loops like yours

The main thing that Go's runtime does is it gives its best to allow everyone to execute and don't starve anyone. How it does that is not specified in the language specification and might change in the future. If the proposal about loops will be implemented then even without function calls switching could occur. At the moment the only thing you should remember is that in some circumstances function calls could cause goroutine to yield execution.

To explain the switching in Akavall's answer, when fmt.Printf is called, the first thing it does is checks whether it needs to grow the stack and calls the scheduler. It MIGHT switch to another goroutine. Whether it will switch depends on the state of other goroutines and exact implementation of the scheduler. Like any scheduler, it probably checks whether there're starving goroutines that should be executed instead. With many iterations function call has greater chance to make a switch because others are starving longer. With few iterations goroutine finishes before starvation happens.

like image 154
m0meni Avatar answered Oct 06 '22 00:10

m0meni