[go]channel

- chan是一个先进先出的队列(管道), 可被close
- 定义
// unbuffered chan
var ch chan int   //ch指向nil
ch = make(chan int)

ch := make(chan int)

// buffered chan
ch := make(chan int, 10) // cap为10, 即chan最大容纳元素个数,代表缓存大小


- 如果cap为0, 则sender和receiver的communication会被block
- 如果cap不为0,则只有buffer满后才会被block

chan

//无缓冲(unbuffered)chan: 必须在两个goroutine之间使用,否则死锁
func main() {
        ch:=make(chan int)
        ch<-1
        fmt.Println(<-ch)
}

//fatal error: all goroutines are asleep - deadlock!

// 无缓冲chan: 读写端都在线, 不会死锁
func main() {
        ch := make(chan int)
        go func() {
                ch <- 1
        }()
        fmt.Println(<-ch)
        time.Sleep(time.Second)
}
//1

//无缓冲chan: 视写端不在线, 死锁
func main() {
        ch := make(chan int)
        fmt.Println(<-ch)
        go func() {
                ch <- 1
        }()
        time.Sleep(time.Second)
}
//fatal error: all goroutines are asleep - deadlock!


小结: 无缓冲chan避免死锁
    1.定义chan
    2.另一个协程操作ch
    3.本协程协同处理ch
//有缓冲(buffered)chan: 在一个goroutine也能玩
func main() {
        ch:=make(chan int,1)
        ch<-1
        fmt.Println(<-ch)
}

//1

// 有缓冲chan
//    1.异步:不必同时在线
//    2.可控制并发
func main() {
    ch := make(chan int, 1)
    ch <- 3
}
- 无缓冲chan避免死锁:

//关闭chan
func main() {
        ch:=make(chan int,1)
        close(ch)
        fmt.Println(<-ch)
}

//关闭chan
func main() {
    ch := make(chan int)
    go func() {
        time.Sleep(time.Second)
        fmt.Println("test")
        close(ch)
    }()
    <-ch
}

//写数据(正常使用)
func main() {
        ch := make(chan int)
        go func() {
                time.Sleep(time.Second)
                fmt.Println("test")
                ch <- 1
        }()
        <-ch
}

channel特性

// 已关闭的在关闭会pannic
func main() {
    ch := make(chan int)
    close(ch)
    close(ch) //panic: close of closed channel
}
// 已关闭的在存值会panic
func main() {
    ch := make(chan int, 2)
    close(ch)
    ch <- 10 //panic: send on closed channel
}
// 已关闭对取值,直到取到其零值
func main() {
    ch := make(chan int)

    go func() {
        for i:=0;i<12;i++ {
            fmt.Println(<-ch)
        }
    }()
    ch<-10
    close(ch)
    time.Sleep(time.Second)
}
//10
//0

// 关闭的chanel可以读值
func main() {
        ch := make(chan int)
        close(ch)
        fmt.Println(<-ch)
}

func main() {
    ch := make(chan int)
    go func() {
        fmt.Println(<-ch)
    }()
    ch <- 10
}
//正常的chan,读写都要在线,否则阻塞等待
func main() {
        ch := make(chan int)
        go func() {
                time.Sleep(time.Second)
                ch <- 1
        }()
        fmt.Println(<-ch) //等待1s后输出
}

close(遍历)chan

// for遍历: 前提是必须知道数据的数量

func main() {
        ch := make(chan int)
        go func() {
                for i := 0; i < 3; i++ {
                        ch <- i
                }
        }()
        for i := 0; i < 3; i++ {
                fmt.Println(<-ch)
        }
}
// ok法检测
func main() {
        ch := make(chan int)
        go func() {
                for i := 0; i < 3; i++ {
                        ch <- i
                }
                close(ch)
        }()
        for {
                num, ok := <-ch
                if ok {
                        fmt.Println(num)
                } else {
                        fmt.Println("closed")
                        break
                }
        }
}
//for range遍历

func main() {
        ch := make(chan int)
        go func() {
                for i := 0; i < 3; i++ {
                        ch <- i
                }
                close(ch)
        }()
        for num := range ch {
                fmt.Println(num)
        }
}

chan使用例子

//应用: 打印机例子
func printer(s string) {
    for _, v := range s {
        fmt.Printf("%c", v)
        time.Sleep(time.Second / 4)
    }
}

func main() {
    ch := make(chan bool)
    go func() {
        printer("hello")
        ch <- true
    }()
    go func() {
        <-ch
        printer("world")
    }()

    time.Sleep(time.Second * 3)
}
//控制两个协程先后顺序
func main() {
    var ch = make(chan bool)
    go func() {//等我结束了你在走.
        for i := 0; i < 10; i++ {
            fmt.Println(i)
        }
        ch<-true
    }()

    <-ch
    fmt.Println("main")
}
//两个协程之间: 读协程感知关闭
func main() {
    var ch = make(chan int)
    go func() { //等我结束了你在走.
        for i := 0; i < 5; i++ {
            ch <- i
            fmt.Println("zi write: ", i) //是一种io操作
        }
    }()
    for i := 0; i < 5; i++ {
        fmt.Println("main read: ", <-ch) //还没来及打印,有被抢占了
    }
}

// 两个协程之间: 等待chan通知另一个协程结束
func main() {
    ch := make(chan int)
    go func() {
        for i := 0; i < 3; i++ {
            ch <- i
        }
        close(ch)
    }()
    for num := range ch { //另一个办法:  num,ok:=<-ch
        fmt.Println(num)
    }
}
//两个协程之间: 生产者消费者
func product(ch chan int) {
        for i := 0; i < 10; i++ {
                ch <- i
        }
        close(ch)
}

func consumer(ch chan int) {
    for v:= range ch {
        fmt.Println(v)
    }
}

func main() {
        ch := make(chan int)
        go product(ch)
        go consumer(ch)

        time.Sleep(time.Second)
}
//多个协程之间: main等待子协程干完活
func main() {
        ch := make(chan int, 10)
        //让10个人去干活
        for i := 0; i < 10; i++ {
                go func(i int) {
                        ch <- i
                        time.Sleep(time.Microsecond)
                }(i)
        }

        //获取10个结果
        for i := 0; i < 10; i++ {
        fmt.Println(<-ch)
        }
}
// 举一个有趣的列子:使用channel将异步变为同步

题目如下:
存在以下代码,请问如何实现print函数可以顺序输出1~75,要求不使用锁,只能使用channel

func main() {
        for i := 1; i < 75; i++ {
                go print(i)
        }
        time.Sleep(5*time.Second) //wait all goroutine
}

func print(i int){
        
}
————————————————

// - 实现
var ch = make(chan int)

func main() {

        for i := 1; i < 75; i++ {
                go printer(i)
                ch <- i
        }
        time.Sleep(5 * time.Second) //wait all goroutine
}

func printer(i int) {
        <-ch
        fmt.Println(i)
}

死锁

//出现死锁
1.写端阻塞

func main() {
        ch:=make(chan int)
        ch<-1
        fmt.Println(<-ch)
}
2.读阻塞
func main() {
        ch:=make(chan int)
        ch<-1
        go func() {
                fmt.Println(<-ch)
        }()
}

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

3.交叉死锁

func main() {
        ch := make(chan int)
        ch2 := make(chan int)
        go func() {
                for {
                        select {
                        case v := <-ch:
                                ch2 <- v
                        }
                }
        }()

        for {
                select {
                case v := <-ch2:
                        ch <- v
                }
        }
}