Go 信道(channel)

什么是信道?

信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。

信道的声明

package main

import (
    "fmt"
    "time"
)

//不同goroutine之间通信
//通过channel实现

func main() {
    //1 定义channel
    var c chan int
    //2 信道的零值(引用类型,空值为nil,当做参数传递时,不需要取地址,改的就是原来的,需要初始化再使用)
    fmt.Println(c)
    //3 信道初始化
    c=make(chan int)  //数字暂时先不关注
    //4 信道的放值  (注意赋值和放值)
    //c<-1
    //c=12  //赋值报错
    //5 信道取值
    //<-c
    //6 取出来赋值给一个变量 int
    //var a int
    //a=<-c
    //a:=<-c

    //7 信道默认不管放值还是取值,都是阻塞的

    //c是引用类型
    go test1(c)

    a:=<-c  //阻塞   不但实现了两条协程之间通信,还实现了等待协程执行结束
    fmt.Println(a)

}

func test1(a chan int)  {
    fmt.Println("go go go ")
    time.Sleep(1*time.Second)
    //往信道中放一个值
    a<-10 //阻塞


}

信道小例子

package main

//信道小例子
//程序有一个数中 每一位的平方和与立方和,然后把平方和与立方和相加并打印出来

import (
    "fmt"
    "time"
)

func calcSquares(number int, squareop chan int) {
    sum := 0  //总和
    for number != 0 {
        digit := number % 10   //589对10取余数,9   8   5
        sum += digit * digit  //sum=9*9   8*8     5*5
        number /= 10         //num=58    5       0
    }
    time.Sleep(2*time.Second)
    squareop <- sum
}

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

//func calcSquares(number int, squareop chan int) int{
//    sum := 0  //总和
//    for number != 0 {
//        digit := number % 10   //589对10取余数,9   8   5
//        sum += digit * digit  //sum=9*9   8*8     5*5
//        number /= 10         //num=58    5       0
//    }
//    time.Sleep(2*time.Second)
//    return sum
//}
//
//func calcCubes(number int, cubeop chan int) int{
//    sum := 0
//    for number != 0 {
//        digit := number % 10
//        sum += digit * digit * digit
//        number /= 10
//    }
//    time.Sleep(2*time.Second)
//    return sum
//}

func main() {
    ctime:=time.Now().Unix()
    fmt.Println(ctime)
    number := 589
    sqrch := make(chan int)
    cubech := make(chan int)
    //num1:=calcSquares(number, sqrch)
    //num2:=calcCubes(number, cubech)
    go calcSquares(number, sqrch)
    go calcCubes(number, cubech)
    squares, cubes := <-sqrch, <-cubech
    //squares:= <-sqrch
    //cubes:=<-cubech
    fmt.Println("Final output", squares + cubes)
    ttime:=time.Now().Unix()
    fmt.Println(ttime)
    fmt.Println(ttime-ctime)
}

信道的死锁现象

package main

import "fmt"

//信道的死锁现象,默认都是阻塞的,一旦有一个放,没有人取  或者一个人取,没有人放,就会出现死锁

//func main() {
//    //var c chan int =make(chan int )
//
//    //c<-1  //其实放不进去,阻塞在这,就死锁了
//    //<-c     //没有,取不到,阻塞在这,就死锁了
//
//}


//单向信道
//func sendData(sendch chan<- int) {
//    sendch <- 10
//}
//
//func main() {
//    //sendch := make(chan<- int)   //定义了一个只写信道
//    sendch := make(chan int)   //定义了一个可读可写信道
//    go sendData(sendch)        //传到函数中转成只写信道,在goroutine中,只负责写,不能往外读,主协程读
//    fmt.Println(<-sendch)   //只写信道一旦读,就有问题
//}



///3 关闭信道
//func main() {
//    sendch := make(chan int)
//    //关闭信道
//    //close(sendch)
//
//    //信道可以用for循环循环
//}


// 信道关闭close(sendch) ,for循环循环信道,如果不关闭会报死锁,如果关闭了,放不进去,循环结束
func producer(chnl chan int) {
    for i := 0; i < 100; i++ {
        fmt.Println("放入了",i)
        chnl <- i
    }
    close(chnl)
}
func main() {
    ch := make(chan int)
    go producer(ch)
    for v := range ch {
        fmt.Println("Received ",v)
    }
}

缓冲信道

package main

import (
    "fmt"
    "sync"
    "time"
)

//缓冲信道:只在缓冲已满的情况,才会阻塞向缓冲信道,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据

//func main() {
//    var c chan int =make(chan int,6)  //无缓冲信道数字是0
//    c<-1
//    c<-2
//    c<-3
//    c<-4
//    c<-5
//    c<-6
//    //c<-7  //死锁
//    <-c
//    <-c
//    //<-c
//    //<-c
//    //<-c
//    //<-c
//    //<-c // 取空了,死锁(一个goroutine中会出现)
//
//
//    // 2 长度 vs 容量
//    //fmt.Println(len(c))  //目前放了多少
//    //fmt.Println(cap(c)) //可以最多放多少
//
//
//    
//}
//3 WaitGroup  等待所有goroutine执行完成
//4  使用信道如何实现?
func process1(i int,wg *sync.WaitGroup)  {
    fmt.Println("started Goroutine ", i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended
", i)
    //一旦有一个完成,减一
    wg.Done()
}

func main() {
    var wg sync.WaitGroup   //没有初始化,值类型,当做参数传递,需要取地址
    //fmt.Println(wg)
    for i:=0;i<10;i++ {
        wg.Add(1) //启动一个goroutine,add加1
        go process1(i,&wg)
    }

    wg.Wait() // 一直阻塞在这,知道调用了10个done,计数器减到零
}
原文地址:https://www.cnblogs.com/ZhZhang12138/p/14886765.html