Code execution results are shocking

package main
import (
	"time"
	"fmt"
)
func in(d *[]int) {
	fmt.Println(*d)
	time.Sleep(time.Duration(2000000000))
	fmt.Println(*d)
}
func main() {
	a := []int{1,2,3}

	go in(&a)
	a = []int{4,5,6}
	//fmt.Printf("")
	for {
	}
}

The code result is:
[1 2 3]
[1 2 3]
When remove the “//” of comment “fmt.Printf(”")", the code result is:
[4 5 6]
[4 5 6]

This is called “concurrency”.

You will never know which go routine will be run first and how the runtime switches between them.

And on different computers the printed output might differ.

If you want strict boundaries, you’ll need mutexes, waitgroups, semaphores or even naked channels, depending on your exact needs.

I know this “concurrency”. I have try many times with no printf and printf. The result is same. I guess that some goroutine that have syscalls has high priority, but I’m failed to search information of the topic.

Hi. Then you add the fmt.Printf will you get a slight pausing of the main goroutine while it waits for stdout to print. This allows the other go routine to run before it enter the tight for loop and it picks up the new array pointed to by &a.

But I agree with @NobbZ. You should not write to data shared by multiple go routines without using some means of synchronizing. And even if it works it will not on another slightly faster or slower computer or on one with more or less cores than your computer.

1 Like

According to you, the result should be contrary to reality. And the print statement is placed after the assignment, meaning that the instruction is rearranged.
I guess that some goroutine that have syscalls has high priority, but I’m failed to search information of the topic.

Usually the go runtime does switch between go-routines only on i/o boundaries. the runtime might realize, that there is no i/o taking place in the main-go-routine anymore and therefore just switches to another go routine and will never reconsider the main one again.

This is just an assumption though.

This is fascinating, I don’t know why this happens. My first thought was that the variable was not being referenced correctly or some sort of concurrency issue. But, this problem only occurs on the “For” infinite loop. If I put in a sleep timer at the bottom rather than an infinite loop the problem disappears.

Sleep time rather than the infinite loop
https://goplay.space/#gMrEqQ0f_Tb

I don’t think it is too important as the infinite loop is problematic in practice and the code is not runnable as is. It would be interesting to see if there is a use-case that runs into this somehow.

If you are wondering how to fix the concurrency issue, then you should use a WaitGroup which will hold the program until the goroutines have run successfully before it moves on to the rest of the code. I have an example of it with and without a WaitGroup applied.

Without the WaitGroup
https://goplay.space/#FRLTD1GqkQ3

With the WaitGroup
https://goplay.space/#OBGVQJT7KhJ

If you need to prevent a race condition which is the root cause of this problem changing from “[1 2 3]” to “[4 5 6]” then you want to implement a mutex to lock the variable before moving on. I have an example of that as well. Good luck.

Mutex Applied
https://goplay.space/#vuYx5ZMPf1R

WGs, a sleep, mutexes, all of these are implemented on top of channels, and channels are an IO boundary, so this supports my theory.

This assumption seems to be right, but we should search some information to confirm that.

I know that the standard should be written to use a synchronization mechanism such as locks to ensure the correct execution of the program, but I just want to know what happens when I don’t write this way.

what happens when I don’t write this way.

Anything can happen. It’s undefined behavior.

You can have unpredictable results. That’s it, in concurrency if you want a certain order you must do it yourself.

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