Suppose I have a Racer function which compares 2 urls and returns the url which took the shortest time to get a response from it's server.
How does goroutines, threads and processor all work under the hood ?
package main
func Racer(a, b string) (winner string) {
select {
case <- ping(a):
return a
case <- ping(b):
return b
}
}
func ping(url string) chan struct{} {
ch := make(chan struct{})
go func() {
http.Get(url)
close(ch)
}()
return ch
}
func main() {
// should return "fastServer.com"
Racer("slowServer.com", "fastServer.com")
}
My understanding:
- Assuming 1xCPU with 1 core, the main thread runs main() and Racer().
- The Racer() fn runs Ping(a) and Ping(b) so that is also on the main thread.
- The Ping() fn creates a Goroutine which is put on a separate thread and returns immediately.
- Since the main thread is being blocked by the select...case in Racer(), the CPU switches to the other thread.
- Goroutine (a) is run first. It makes a blocking http GET call. This causes the processor to ship off Goroutine (a) to a network thread and run Goroutine (b).
- Goroutine (b) makes a blocking http GET call. Processor ships off Goroutine (b) to network thread.
- main thread running Racer() is back on processor. Still being blocked by select...case.
- Goroutine (b) http GET call finally resolves. The processor runs Goroutine (b) to completion, closing the channel.
- main thread running Racer() is back on processor. select...case satisfies ping(b). Racer() returns b url.
- main function exits.
Is this correct ? assuming url (a) takes a long time.
