Unexpected outputs while using channel in goroutines

Hi!

I’m starting at Go and I have a problem with channels.

The following source have different outputs when I run on my computer or when I run on “Try go” online (https://play.golang.org/)

package main
import "fmt"
func main() {
	ch := make(chan int)
	go func(a chan int) {	
		a <- 1	
		fmt.Println(2)
	}(ch)
	go func(b chan int) {	
		b <- 3	
		fmt.Println(4)
	}(ch)
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

On my computer that has some different outputs which 1 and 3 is printed.
On “Try Go” that has always the same output (4\n3\n2\n1\n).

I’d like to know why those are different.
I thought the output from “Try Go” should be right because channel should stack those goroutines and then unstack them printing that way. Am I wrong? (why?)

Hey @yagotome, you are not syncing your goroutines properly so therefore sometimes both goroutines have time to run before main exits and other times it doesn’t.

The reason that the go playground would probably be consistently showing the same different result than on your computer is because it caches the results of previous compilations which use the same source code and returns a cached response if one is stored in memcache.

This is probably what you want:

package main

import (
	"fmt"
	"sync"
)

func main() {
	// Create a new sync.WaitGroup for synchronizing goroutines.
	wg := new(sync.WaitGroup)

	ch := make(chan int)

	// Add 2 to wg's counter.
	wg.Add(2)

	go func(a chan int, wg *sync.WaitGroup) {
		// Decrement wg's counter.
		defer wg.Done()
		a <- 1
		fmt.Println(2)
	}(ch, wg)

	go func(b chan int, wg *sync.WaitGroup) {
		// Decrement wg's counter.
		defer wg.Done()
		b <- 3
		fmt.Println(4)
	}(ch, wg)

	// Wait till wg's counter is 0 and then close ch.
	go func() {
		wg.Wait()
		close(ch)
	}()

	// Print any received values from ch until ch is closed.
	//
	// Ranging over a channel will infinitely receive every
	// value from a channel until the channel is closed.
	for val := range ch {
		fmt.Println(val)
	}
}

Edit: Since you say you are new to Go, I just re-posted the code but with comments explaining what the code is doing.

1 Like

Everything below is just as I recall reading it somewhere, no guarantees! :wink:

Afaik the go Playground is intentionally deterministic ie there is no randomness but the same code gives the same result every time (esp noticeable on random number generators I think), so that’s why you get the same result there. (Don’t ask me how you even do that)

In a ‘real world’ environment there is no pre-determination which goroutines will get executed when, that depends on the runtime scheduler and on the thread scheduler in your OS I guess. Think of goroutines not as a stack but rather as a pool and the scheduler just picks some and lets them run with no guarantee in which order or even if they will get executed - except when you use syncing as @radovskyb pointed out.

If you want a more detailed / accurate explanation, I’m sure someone can point you to some helpful resources and/or answer your questions :slight_smile:

1 Like

@radovskyb @Jaytn thank you very much for your explanations, I understood what you tow said. However, I still have some doughts.

Go routines run in a different thread from the main, don’t they?

If so, I’d like to understand better how channel works. Doesn’t it work blocking and stacking threads?

I saw that in my source those 2 threads could run in any order depending of scheduler, but would channel stack them after they are called?

Thank you again, guys!

As far as I understand it, in Go you don’t think about threads at all. Goroutines are “things” that can run “concurrently” and it’s the runtime’s job to handle them - which it will do using as much threads as is feasible on your system. I think it will keep running one goroutine per thread until that blocks, eg on a system call (disk/network/…) or waiting for a channel - and then switch to the next one to keep your threads busy. I may be wrong on that one, maybe it interrupts goroutines to handle system calls from other ones, I don’t know. And afaik the main function is just a goroutine like any other, it’s just the one you start with.
//edit not quite, since the program exits when the main function ends, whether other gorutines are still running or not. But it definitely doesn’t necessarily run in a different thread because you can on a single thread machine end up with a “stuck” program when your main function has an infinite loop so your other goroutines will never run.
What you mean by channel stack I don’t understand.


Again, there are ppl here who know far more than me, listen to them, this is just how I wrapped my brain around it.
Also there are some great lectures about concurrency, goroutines and channels on YouTube, that’s mainly how I learned it, but I can’t remember which ones -.- but I’d look there, you can find some of the people who created the language there.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.