Goroutine导致for循环中的可互换动作

I'm very new to go and I'm not sure why this code has this output. I understand that sleep will cause the new goroutine to start the other thread for the specified amount of time. I'm trying to sequentially map out the logic and it looks like "world" should always print before "hello".

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(1 * time.Millisecond)
        fmt.Println(s, i)
    }
}

func main() {
    go say("world")
    say("hello")
}

Actual Output:

world 0
hello 0
hello 1
world 1
world 2
hello 2
hello 3
world 3
world 4
hello 4

Expected Output:

world 0
hello 0
world 1
hello 1
world 2
hello 2

...etc

I understand that sleep will cause the new goroutine to start the other thread for the specified amount of time

This is partly incorrect!

This would be correct on a machine with only one core where only one thread can be executed at a time.

On a machine with multiple cores go can execute as many goroutines parallel as there are cores. With parallel executed goroutines there is no guarantee at all, what will be executed before or after.

You cannot say with any certainty what order concurrent operations will "always" execute in. That's how concurrency works. If you want to control the order of execution, either don't execute concurrently, or use synchronization constructs (e.g. mutex, channel) to control the order of operations.

As others have said, there are no guarantees you can assume about the order of execution.

The Go scheduler has an internal algorithm that decides how to allocate the processor and there is little you can do to control this without resorting to synchronization (which would be the right approach to take here).

If you are interested in learning how to control synchronization between tasks, take a look at the sync package, and also at how channels work:

https://golang.org/pkg/sync/

https://tour.golang.org/concurrency/2

However, I want to add something that others have not mentioned, and while it does not allow you to control execution order, it might be worth commenting due to the nature of the question.

There is a runtime.Gosched function that you can use to hint the scheduler. It would yield the processor so it is likely that other threads will execute.

https://golang.org/pkg/runtime/#Gosched

If you add a call to Gosched instead of sleeping, in my tests it is much more likely that "hello" and "world" would output in order (though again, there is no guarantee and they will appear in random order at times).

Try it like this:

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        fmt.Println(s, i)
        runtime.Gosched()
    }
}

func main() {
    go say("world")
    say("hello")
}

Lastly, take a look at this article which you might find interesting as well:

http://container-solutions.com/surprise-golang-thread-scheduling/