0

Go's documentation says that

Gosched yields the processor, allowing other goroutines to run. It does not suspend the current goroutine, so execution resumes automatically.

Based on that definition if I have a series of long running go routines being created and executed concurrently, would it be advantageous to write a select statement the following way:

for {
    select {
        case msg := <- msgch : 
            fmt.Println(msg)
        default: 
            runtime.Gosched()
    }
} 

I assume based on the documentation, this code can result in more go routines being run. Is my assumption correct?

Arnold Zahrneinder
  • 4,788
  • 10
  • 40
  • 76
  • This should be easy enough to test and measure yourself. If not, that probably means you don't have any particular measureable problem you're trying to solve (i.e, this is a premature optimization). – Hymns For Disco Jun 10 '22 at 03:22
  • What exactly does it mean to have "more goroutines being run"? – Hymns For Disco Jun 10 '22 at 03:31
  • @HymnsForDisco: This part of the documentation says `allowing other goroutines to run`, so I assume that while the select is awaiting the channel's response, in the mean while `Gosched` can result in more go routines entering the execution! Is that correct??! – Arnold Zahrneinder Jun 10 '22 at 03:36
  • @HymnsForDisco: For example, if the go routine is waiting for an HTTP response, it is actually doing nothing and the process is IO bound. So does it bring more go routines into the play? – Arnold Zahrneinder Jun 10 '22 at 03:42
  • If the goroutine is waiting for the channel, it will yield the processor automatically. The go scheduler is already capable of using your processors efficiently to run the right goroutines at the right times. Something like Gosched is only a backup for very special circumstances. It is included in the library more for completeness, rather than for regular use. – Hymns For Disco Jun 10 '22 at 03:44
  • @HymnsForDisco Thank you. Would you please provide me with some examples of those special cases? I'm learning Go and I am from C# background. In C# we usually dive deep into everything, I guess the Go community doesn't like that, but I would like to know. – Arnold Zahrneinder Jun 10 '22 at 04:04
  • 1
    Instead of busy-looping, it is better to simply block (without a default case), and let the scheduler do its job. Once a goroutine is blocked, other will run. – Burak Serdar Jun 10 '22 at 04:05
  • @BurakSerdar: Why, can you provide more information on why blocking is better? – Arnold Zahrneinder Jun 10 '22 at 04:07
  • @BurakSerdar: I guess you meant, it is simpler (not necessarily better), is that correct? – Arnold Zahrneinder Jun 10 '22 at 04:08
  • I C# there is a difference between Blocking and Suspension. Is the same in Go? – Arnold Zahrneinder Jun 10 '22 at 04:10
  • 1
    It is both simpler and better. Remove select, do a blocking read from the channel. Fewer statements. You will save a few cycles if the channel is not ready. – Burak Serdar Jun 10 '22 at 04:15
  • I can't really give a good list of examples, here's why: The compiler automatically inserts scheduler checkpoints into your compiled program wherever it sees fit. The only time you would need Gosched is if the compiler fails to do so at some important section. As @Ben Hoyt explained in their answer, the compiler can and does update and improve this capability over time, and with modern releases you probably will never need it. – Hymns For Disco Jun 10 '22 at 04:39
  • 1
    Blocking vs Suspension: As long as code is "blocking", the subsequent code will not execute. The word "suspend" is not specially defined in the language specification nor the runtime package; in the comment you quoted, it's just used to assure you that no additional action is needed to allow that goroutine to resume executing. – Hymns For Disco Jun 10 '22 at 04:45

2 Answers2

4

No, it isn't necessary here, because whenever Go is waiting on a channel or waiting for I/O, it allows other goroutines to run automatically. That's been the case since Go 1.0.

In Go 1.2 the Go runtime's scheduler added automatic preemption points whenever you called a function. Prior to that if you had a CPU-bound loop (even with a function call) it could starve the scheduler and you might need runtime.Gosched.

And then in Go 1.14, they made this aspect of the runtime even better, and even tight CPU-bound loops with no functions calls are automatically pre-empted.

So with any Go version, you don't need to call runtime.Gosched when you're just waiting on a channel or on I/O; before 1.14, you may have wanted to call it if you were doing a long-running calculation. But with Go 1.14+, I don't see why you'd ever need to call it manually.


If I was reviewing your actual code, I'd suggest changing it to a simple for ... range loop:

for msg := range msgCh {
    fmt.Println(msg)
}

This will wait for each message to come in and print it, and stop if/when the channel is closed. However, you would want a switch if you're waiting on another channel or done signal, for example a context. Something like this:

for {
    select {
        case msg := <- msgCh:
            fmt.Println(msg)
        case <-ctx.Done():
            return
    }
}
Ben Hoyt
  • 10,694
  • 5
  • 60
  • 84
  • Not related to my question, can a long running go routine be garbage collected? specially if you run it and then forget about it (fire go routine and then return)? – Arnold Zahrneinder Jun 10 '22 at 08:28
  • In C#, the thread pool prevents this from happening (sorry I need to compare go with a language that I know very well to understand it better) – Arnold Zahrneinder Jun 10 '22 at 08:29
  • @ArnoldZahrneinder Goroutines do not have an exposed id, or any other way to reference them in the language. The goroutine spawned by `go f()` will continue to run until execution of `f` terminates (either by `return` or `panic()` or runtime error). If you want to externally control the execution of a goroutine and terminate it later, you must write that logic using something such as a [`context`](https://pkg.go.dev/context) like shown in the answer, or a "quit" channel. – Hymns For Disco Jun 10 '22 at 23:17
  • See [How to stop a goroutine](https://stackoverflow.com/q/6807590/11424673) – Hymns For Disco Jun 10 '22 at 23:20
1

Does using runtime.Gosched() [anywhere] make any sense?

No. It basically is never needed or sensible to use Gosched.

Volker
  • 40,468
  • 7
  • 81
  • 87