Go-Goroutine&Channel

goroutine(协程)

package main

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

// 声明goroutine 计数器
var wg sync.WaitGroup

func test() {
    defer wg.Done() //// goroutine 结束就登记-1
    for i := 0; i < 10; i++ {
        fmt.Println("test go...")
        time.Sleep(time.Millisecond * 100)
    }

}

func main() {
    wg.Add(1) // 计数器+1
    go test() // go fun()开启一个协程
    for i := 0; i < 10; i++ {
        fmt.Println("main go...")
        time.Sleep(time.Millisecond * 50)
    }

    wg.Wait() //等待所有登记的 goroutine 都结束
    fmt.Println("main 退出")
}

channel(管道)

作用:goroutine 之间的通信

定义使用

package main

import "fmt"

func main() {
    // channel  是先进先出原则
    //声明管道
    //var 变量 chan 元素类型
    //举几个例子
    //var ch1 chan int
    // 声明一个传递整型的管道
    //var ch2 chan bool
    // 声明一个传递布尔型的管道
    //var ch3 chan []int // 声明一个传递 int 切片的管道
    //var aa chan int
    //fmt.Println(aa)

    //定义管道  chan 类型,3是容量
    ch5 := make(chan int, 3)
    ch5 <- 1
    ch5 <- 2
    fmt.Println(<-ch5)
    fmt.Println(<-ch5)

    close(ch5) // 关闭管道
    ch5 <- 3   // 关闭之后就不能继续send内容了
    fmt.Println(<-ch5)
}

关闭后的管道有以下特点:

对一个关闭的管道再发送值就会导致 panic。
对一个关闭的管道进行接收会一直获取值直到管道为空。
对一个关闭的并且没有值的管道执行接收操作会得到对应类型的零值。
关闭一个已经关闭的管道会导致 panic。

遍历

package main

import "fmt"

func main() {
    var ch1 = make(chan int, 5)
    for i := 0; i < 5; i++ {
        ch1 <- i + 1
    }
    close(ch1) //关闭管道
    //使用 for range 遍历管道,当管道被关闭的时候就会退出 for range,如果没有关闭管道
    //就会报个错误 fatal error: all goroutines are asleep - deadlock!
    //通过 for range 来遍历管道数据
    //管道没有 key
    for val := range ch1 {
        fmt.Println(val)
    }
    /*
        1
        2
        3
        4
        5
    */
}

单通道

package main

import "fmt"

func main() {
    //chan<-  表示只可写入  不能读取
    ch1 := make(chan<- int, 3)
    ch1 <- 1
    ch1 <- 2
    fmt.Println(ch1) //  读取失败
    //<-chan 表示只读  不能写入
    ch2 := make(<-chan int, 6)
    ch2 <- 1 // 写入失败
}

select多路复用

 

在某些场景下我们需要同时从多个通道接收数据。这个时候就可以用到 golang 中给我们提供的 select 多路复用。

 

通常情况通道在接收数据时,如果没有数据可以接收将会发生阻塞。

 

select 的使用类似于 switch 语句,它有一系列 case 分支和一个默认的分支。每个 case 会对

应一个管道的通信(接收或发送)过程。select 会一直等待,直到某个 case 的通信操作完成
时,就会执行 case 分支对应的语句。具体格式如下:

 

记得一定要 return

select{
case <-ch1:
...
case data := <-ch2:
...
case ch3<-data:
...
package main

import "fmt"

func main() {
    ch1 := make(chan int, 3)
    ch1 <- 1
    ch1 <- 2
    ch1 <- 3
    ch2 := make(chan int, 3)
    ch2 <- 4
    ch2 <- 5
    ch2 <- 6
    for {
        select {
        case v := <-ch1:
            fmt.Println(v)
        case v2 := <-ch2:
            fmt.Println(v2)
        default:
            fmt.Println("打印完毕")
            return //一定要注意
        }
    }

    /*
        1
        4
        2
        3
        5
        6
        打印完毕
    */

}

 

posted @ 2020-01-17 09:49  GJH-  阅读(98)  评论(0)    收藏  举报