We can use channels in order to send information among concurrent goroutines. They can be thought of as pipes in which we pass data around. A new channel is created using the syntax:
make(chan <dataType>)
// e.g. a channel for passing strings
myChannel := make(chan string)
We can then send values through the channel using the syntax:
myChannel <- "this is my message to you"
Receiving values from a channel
In order to get a value in a channel, we use the syntax:
// store value from channel into variable myMessage
myMessage := <-myChannel
Consider the following code:
package main
import "fmt"
func main() {
littleBirdie := make(chan string)
myMessages := []string{
"Lester is ok",
"Nana is at least 60 now",
}
for _, message := range myMessages {
go func() {
littleBirdie <- message
}()
}
// get first message
message := <-littleBirdie
// print first message
fmt.Println(message)
// print second message
fmt.Println(<-littleBirdie)
}
channels.go Copy
Channels are unbuffered
Channels are, by default, unbuffered meaning they only accept sends (chan <-) once there is a corresponding receive (<- chan) in the code. A deadlock occurs when there is a send and receive operation in the same goroutine:
package main
import "fmt"
func main() {
bits := make(chan int)
bits <- 1
fmt.Println("Sent a bit of 1")
fmt.Println("Received", <-bits)
}
channels-deadlock.go Copy
Deadlocks can also occur if the receive operations exceed the number of values passed into the channel:
package main
import "fmt"
func main() {
bits := make(chan int)
go func() {
bits <- 1
bits <- 0
}()
fmt.Println("Sent a bit of 1")
fmt.Println("Received", <-bits)
fmt.Println("Received", <-bits)
// asking for too many values
fmt.Println("Received", <-bits)
}
channels-deadlock-2.go Copy