Golang-channel通道初识

1.通道可以被认为是Goroutines通信的管道;当多个goroutines想共享数据的时候,虽然也提供了传统的同步机制(同步等待组、同步锁),但是Go语言强烈建议的是使用Channel通道来实现goroutine之间的通信;

2.通道的声明和定义一个变量的语法一样; 通道是引用型数据

//声明通道
var 通道名 chan 数据类型
//创建通道:如果通道为nil(就是不存在),就需要先创建通道
通道名 = make(chan 数据类型)

func main(){
    var a chan int
    if a == nil {
        fmt.Println("channel 是nil的,不能使用,需要先创建通道。")
    }
    a = make(chan int)
    fmt.Printf("数据类型:%T", a)

}

//也可以简短声明:
a := make(chan int)

3.通道的注意点

  • 用于goroutine,传递消息
  • 每个通道都有相关联的数据类型
  • 使用通道传递数据 <-
  • chan <-data,发送数据到通道。向通道中写数据
  • data <-chan,从通道中获取数据。从通道中读取数据
  • 阻塞:发送数据 chan <- data,阻塞的,直到另一条goroutine,读取数据来解除阻塞;读取数据,data <- chan,也是阻塞的。直到另一条goroutine,写出数据解除阻塞;
  • 本身channel就是同步的,意味着同一时间,只能有一条goroutine来操作
func main() {
    var ch1 chan bool
    ch1 = make(chan bool)

    go func() {
        for i:=0; i<10; i++ {
            fmt.Println("子goroutine中,i:", i)
        }
        //循环结束,向通道中写数据,表示要结束了。。。
        ch1 <- true
        fmt.Println("结束")
    }()


    //如果主进程先抢占资源,会去chan中读数据,如果没有数据,会阻塞下去..
    data := <- ch1
    fmt.Println("main...data-->:", data)
    fmt.Println("main主进程结束...")
}
//阻塞式通道
func main() {
    ch1 := make(chan int)

    go func() {
        fmt.Println("子goroutine开始执行")

        //time.Sleep(2*time.Second)
        data := <- ch1 //从ch1中读取数据
        fmt.Println("data:", data)
    }()

    //time.Sleep(5*time.Second)
    ch1 <- 10
    fmt.Println("main...over...")
}
//死锁:使用通道的时候要考虑的一个重要因素;只有发送数据,没有接受数据;或者只有接受数据,没有发送数据

func main()  {
    ch1 := make(chan int)
    ch1 <- 5
}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()

3.关闭通道和通道上范围循环

· 关闭通道,发送者可以关闭通道,来通知接受方不会有更多的数据发送到channel上: close(ch);接受者可以在接收来自通道的数据时使用额外的变量来检查通道是否已经关闭:v,ok := <-ch, 如果ok的值是true,表示成功的读取了一个数据value。如果为false,意味着正在从一个封闭的通道读取数据,从封闭通道读取的值为零值(根据ch的类型的零值)

关闭通道:close(ch)
    子goroutine:写出10个数据
        每写一个,阻塞一次,主goroutine读取一次,解除阻塞

    主goroutine,读取数据
        每次读取数据,阻塞一次,子goroutine,写出一个,解除阻塞

func main() {
    
    ch1 := make(chan int)
    go sendData(ch1)

    for{
        time.Sleep(1*time.Second)
        v, ok := <- ch1
        if !ok {
            fmt.Println("已经读取了所有的数据。。。", ok)
            break
        }
        fmt.Println("读取的数据:", v, ok)
    }
    fmt.Println("main..over...")

}

func sendData(ch1 chan int)  {
    //发送方:10条数据
    for i:=0; i <10; i++ {
        ch1 <- i
    }
    close(ch1)
}

读取的数据: 0 true
读取的数据: 1 true
读取的数据: 2 true
读取的数据: 3 true
读取的数据: 4 true
读取的数据: 5 true
读取的数据: 6 true
读取的数据: 7 true
读取的数据: 8 true
读取的数据: 9 true
已经读取了所有的数据。。。 false
main..over...
  • 通道上的范围循环
func main() {

    ch1 := make(chan int)
    go sendData(ch1)

    //for循环的 for range形式可用于从通道接收值,直到它关闭为止,不需要单独判断
    for v := range ch1{//从通道读取数据,如果没值,则阻塞
        fmt.Println("读取数据:", v)
    }
    fmt.Println("main..over.....")

}

func sendData(ch1 chan int)  {
    //发送方:10条数据
    for i:=0; i <10; i++ {
        time.Sleep(1*time.Second)
        ch1 <- i
    }
    close(ch1)//通知对方,通道关闭;否则会发生死锁
}

标签: Golang

添加新评论