문제

Consider this program:

package main

import "fmt"
import "time"
import "runtime"

func main() {

    x := 0

    go func() {
        time.Sleep(500 * time.Millisecond)
        x = 1
    }()

    for x == 0 {
        runtime.Gosched()
    }

    fmt.Println("it works!")
}

Why does it terminate locally but not on Playground? Does the termination of my program rely on undefined behavior?

도움이 되었습니까?

해결책 3

That code doesn't offer much guarantees. It's relying almost entirely on implementation details around undefined behavior.

In most multi-threaded systems, there's no guarantee that a change in one thread with no barrier will be seen in another. You've got a goroutine which could be running on another processor altogether writing a value to a variable nobody's ever guaranteed to read.

The for x == 0 { could quite easily be rewritten to for { since there's never a guarantee any changes to that variable might be visible.

The race detector will also probably report this issue. You should really not expect this to work. If you want a sync.WaitGroup you should just use one as it properly coordinates across threads.

다른 팁

The Go playground uses a special implementation of time.Sleep designed to prevent individual programs from monopolising the back end resources of the website.

As described in this article about the how the playground is implemented, goroutines that call time.Sleep() are put to sleep. The playground back end waits until all other goroutines are blocked (what would otherwise be a deadlock), and then wakes the goroutine with the shortest time out.

In your program, you have two goroutines: the main one, and one that calls time.Sleep. Since the main goroutine never blocks, the time.Sleep call will never return. The program continues until it exceeds the CPU time allocated to it and is then terminated.

The Go Memory Model does not guarantee that the value written to x in the goroutine will ever be observed by the main program. A similarly erroneous program is given as an example in the section on go routine destruction. The Go Memory Model also specifically calls out busy waiting without synchronization as an incorrect idiom in this section.

You need to do some kind of synchronization in the goroutine in order to guarantee that x=1 happens before one of the iterations of the for loop in main.

Here is a version of the program that is guaranteed to work as intended.

http://play.golang.org/p/s3t5_-Q73W

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan bool)
    x := 0

    go func() {
        time.Sleep(500 * time.Millisecond)
        x = 1
        close(c) // 1
    }()

    for x == 0 {
        <-c // 2
    }

    fmt.Println("it works!")
}

The Go Memory Model guarantees that the line marked with // 1 happens before the line marked with // 2. As a result, the for loop is guaranteed to terminate before its second iteration.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top