22 Channels

83 阅读3分钟

Channels 是 Goroutines communicate 的通道。

Declaring channels

package main

import "fmt"

func main() {
	var a chan int
	if a == nil {
		fmt.Println("channel a is nil, going to define it")
		a = make(chan int)
		fmt.Printf("Type of a is %T", a)
	}
}

Sending and receiving from a channel

data := <- a // read from channel a 
a <- data // write to channel a

Sends and receives are blocking by default

  • When data is sent to a channel, the control is blocked in the send statement until some other Goroutine reads from that channel.
  • Similarly, when data is read from a channel, the read is blocked until some Goroutine writes data to that channel.

Channel example program

package main

import (
	"fmt"
)

func hello(done chan bool) {
	fmt.Println("Hello world goroutine")
	done <- true
}
func main() {
	done := make(chan bool)
	go hello(done)
	<-done
	fmt.Println("main function")
}

// Hello world goroutine
// main function

In line no. 14 we are receiving data from the done channel. This line of code is blocking which means that until some Goroutine writes data to the done channel, the control will not move to the next line of code.

TIP TIP

  • 当你给Channel生产数据,没有消费者,你会被阻塞。(生产的过程是唯一主动的入口
  • 当你消费Channel的数据,没有生产者,你会被阻塞。
  • 如果没有消费者,或者生产存在,则会死锁 ( will panic. fatal error: all goroutines are asleep - deadlock!
package main

import (
	"fmt"
	"time"
)

func hello(done chan bool) {
	fmt.Println("hello go routine is going to sleep") //2
	time.Sleep(4 * time.Second) //3 sleep 4 seconds
	fmt.Println("hello go routine awake and going to write to done") //5
	done <- true //6
}
func main() {
	done := make(chan bool)
	fmt.Println("Main going to call hello go goroutine") // 1
	go hello(done) 
	<-done //7   wait which goroutine add data to this channel
	fmt.Println("Main received data")
}

Another example for channels

package main

import (  
    "fmt"
)

func calcSquares(number int, squareop chan int) {  
    sum := 0
    for number != 0 {
        digit := number % 10
        sum += digit * digit
        number /= 10
    }
    squareop <- sum
}

func calcCubes(number int, cubeop chan int) {  
    sum := 0 
    for number != 0 {
        digit := number % 10
        sum += digit * digit * digit
        number /= 10
    }
    cubeop <- sum
} 

func main() {  
    number := 589
    sqrch := make(chan int)
    cubech := make(chan int)
    go calcSquares(number, sqrch)
    go calcCubes(number, cubech)
    squares, cubes := <-sqrch, <-cubech
    fmt.Println("Final output", squares + cubes)
}

主程序通过两个goroutine,去做square cube计算。等两个结果都算出来了,然后结束。

Deadlock

TIP

  • 当你给Channel生产数据,没有消费者,你会被阻塞。(生产的过程是唯一主动的入口
  • 当你消费Channel的数据,没有生产者,你会被阻塞。
  • 如果没有消费者,或者生产存在,则会死锁 (will panic. fatal error: all goroutines are asleep - deadlock!
package main


func main() {
	ch := make(chan int)
	ch <- 5
}

Unidirectional channels

All the channels we discussed so far are bidirectional channels

package main

import "fmt"

func sendData(sendch chan<- int) {
	sendch <- 10  //生产
}

func main() {
	sendch := make(chan<- int)  //创建一个生产者only 
	go sendData(sendch)
	fmt.Println(<-sendch)   //消费  will panic
}
package main

import "fmt"

func sendData(sendch chan<- int) { //auto convert bidirectional to Unidirectional
	sendch <- 10
}

func main() {
	sendch := make(chan<- int)
	go sendData(sendch)
	fmt.Println(<-sendch)
}

Closing channels and for range loops on channels

  • Senders have the ability to close the channel to notify receivers that no more data will be sent on the channel.
  • Receivers can use an additional variable while receiving data from the channel to check whether the channel has been closed.
v, ok := <- ch 

In the above statement ok is true if the value was received by a successful send operation to a channel. If ok is false it means that we are reading from a closed channel. The value read from a closed channel will be the zero value of the channel’s type. For example, if the channel is an int channel, then the value received from a closed channel will be 0.

package main

import (
	"fmt"
)

func producer(chnl chan int) {
	for i := 0; i < 10; i++ {
		chnl <- i
	}
	close(chnl)
}
func main() {
	ch := make(chan int)
	go producer(ch)
	for {
		v, ok := <-ch
		if ok == false {
			break
		}
		fmt.Println("Received ", v, ok)
	}
}

Received  0 true
Received  1 true
Received  2 true
Received  3 true
Received  4 true
Received  5 true
Received  6 true
Received  7 true
Received  8 true
Received  9 true

The for range form of the for loop can be used to receive values from a channel until it is closed.

package main

import (
	"fmt"
)

func producer(chnl chan int) {
	for i := 0; i < 10; i++ {
		chnl <- i
	}
	close(chnl)
}
func main() {
	ch := make(chan int)
	go producer(ch)
	for v := range ch {
		fmt.Println("Received ",v)
	}
}

Optimize Square、Cube、Calculate loigc.

package main

import (
	"fmt"
)

func digits(number int, dchnl chan int) {
	for number != 0 {
		digit := number % 10
		dchnl <- digit
		number /= 10
	}
	close(dchnl)
}
func calcSquares(number int, squareop chan int) {
	sum := 0
	dch := make(chan int)
	go digits(number, dch)
	for digit := range dch {
		sum += digit * digit
	}
	squareop <- sum
}

func calcCubes(number int, cubeop chan int) {
	sum := 0
	dch := make(chan int)
	go digits(number, dch)
	for digit := range dch {
		sum += digit * digit * digit
	}
	cubeop <- sum
}

func main() {
	number := 589
	sqrch := make(chan int)
	cubech := make(chan int)
	go calcSquares(number, sqrch)
	go calcCubes(number, cubech)
	squares, cubes := <-sqrch, <-cubech
	fmt.Println("Final output", squares+cubes)
}