简体   繁体   中英

Race condition with a simple channel in Go?

I'm new to Go and am stumped on what appears to be a somewhat-rare race condition with a very small block of code running on Linux with Go version 1.2.

Basically, I create a channel for an int , start a go routine to read from the channel, and then write a single int to the channel.

package main

import "fmt"

func main() {
    channel := make(chan int)

    go func() {
        number := <- channel
        fmt.Printf("GOT IT: %d\n", number)
    }()

    fmt.Println("[+] putting num on channel")
    channel <- 42
    fmt.Println("[-] putting num on channel")
}

The output about 90% of the time is as expected:

$ go run test.go 
[+] putting num on channel
GOT IT: 42
[-] putting num on channel

However, about 10% of the time, the go routine simply does not read the number from the channel and prints nothing:

$ go run test.go 
[+] putting num on channel
[-] putting num on channel

I'm puzzled because this code is very similar to the example at https://gobyexample.com/channels , (which I do not have this problem with) except that I'm reading from the channel in my go routine instead of writing to the channel.

Do I have a fundamental misunderstanding of how channels work or is there something else at play here?

You should wait until your goroutine executes, and then your, for example, you can do it with sync.WaitGroup :

package main

import (
  "fmt"
  "sync"
)

func main() {
  var wg sync.WaitGroup

  channel := make(chan int)
  wg.Add(1)

  go func() {
    number := <-channel
    fmt.Printf("GOT IT: %d\n", number)
    wg.Done()
  }()

  fmt.Println("[+] putting num on channel")
  channel <- 42
  wg.Wait()
  fmt.Println("[-] putting num on channel")
}

(goplay: http://play.golang.org/p/VycxTw_4vu )

Also you can do it with a "notification channel", that indicates that job is done:

package main

import "fmt"

func main() {
  channel := make(chan int)
  done := make(chan bool)

  go func() {
    number := <-channel
    fmt.Printf("GOT IT: %d\n", number)
    done <- true
  }()

  fmt.Println("[+] putting num on channel")
  channel <- 42
  <-done
  fmt.Println("[-] putting num on channel")
}

(goplay: http://play.golang.org/p/fApWQgtr4D )

You seem to be expecting the receiving goroutine to run to completion before the second fmt.Println executes. This is not guaranteed to be the case. If the program terminates, goroutines are not guaranteed to reach the end of their functions.

When you see the output that doesn't display the "GOT IT" message, the channel delivered its message, but the main function completed before the goroutine did. The program terminated, and the goroutine never gets the chance to call fmt.Printf

In the example you cited, the main function ends with this:

go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)

Since the main function blocks until it receives a message, the goroutine always runs to completion in this example. In your code, your goroutine executes a step after it receives from the channel, and it's undefined whether the goroutine or the main function will execute the next line after the receive.

You have two goroutines, one in main() (which is implicitly a goroutine), and the anonymous one.

They communicate over a synchronous channel, so after the channel communication, it's guaranteed that they're synchronised.

At this point, the code left in the main() goroutine looks like this:

fmt.Println("[-] putting num on a channel")

and the code left in the anonymous goroutine looks like this:

fmt.Println("GOT IT: %d\n", number)

Now you're racing: the output from these Println s may appear in either order, or even intermingled. When the Println() from main finishes, the next thing that will happen on that goroutine is that your program will be stopped. This may prevent some or all of the Println from the anonymous goroutine from appearing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM