Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Race condition with a simple channel in Go?

Tags:

go

channel

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?

like image 338
shanet Avatar asked Dec 07 '22 04:12

shanet


1 Answers

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)

like image 169
Kavu Avatar answered Dec 19 '22 15:12

Kavu