Golang高级编程: 并发篇 (一) 通道 + 通道超时

    技术2022-07-10  163

    简介

    读取和写通道超时, 是很多人在使用通道过程中经常遇到的, 下面就介绍一下通道,然后简单的对读写通道做超时处理

     

    通道概念 

     1. 通道采用的是FIFO(先进先出)

     2. 通道的创建

        chn := make(chan int, 0) // 创建读写int类型的通道, 0 代表着缓存为0,即写同时必须读取, 否则将堵塞

        readOnlyChn := make(<-chan int) // 只读通道

        writeOnlyChn := make(chan<- int) // 只写通道

    3. 关闭通道

        i.  读通道直接返回0(整数类型),bytes则返回[], 

        ii. 写通道 会抛出异常 (send on closed channel)

    4. 判断通道是否关闭

       v, ok := <-chn // ok=true说明正常,ok=false说明通道关闭

    5. 通道置为nil

        i. 读写通道时都会死锁

        ii. select 前置 nil,则select时直接忽略,select后没有其它的case则同样堵塞

     

    示例

    基本使用场景一

    package main import ( "fmt" "time" ) func recv(chn chan int) { for { select { case v, ok := <-chn: fmt.Println("value: ", v, "; ok: ", ok) if !ok { fmt.Println("channel closed!!! ") return // 退出协程 } default: fmt.Println("recv alive print.") time.Sleep(time.Second * 1) } } } func main() { var chn = make(chan int, 0) //var readOnlyChn = make(<-chan int) //var writeOnlyChn = make(chan<- int) /* 先启动接收协程, 在通道写入数据的时候进行读出, 由于之前创建了 0 缓存的通道, 写不同时读出造成堵塞 */ go recv(chn) chn<-1024 // 写入1024数据 time.Sleep(time.Second * 2) // 延时两秒, 好让接收线程接收处理写入数据 chn = nil //close(chn) // 关闭通道 time.Sleep(5 * time.Second) }

    上面的使用中, 当通道被置为nil的时候虽然由于加了default不会造成死锁, 但default依然会进行不断的打印执行;

    有人说了, 可以在default时判断通道是否为nil, 思路是对的,但是却没那么简单, Go中只有值传递, 即你在传通道这个参数的时候是对通道引用的拷贝, 而不是其本身, 所以通道置nil,其实recv函数内部的chn还是非空的

    使用场景二, 一场景升华版

    这样, 不管是通道为nil还是关闭通道都能够正常监控到了, 并及时退出协程了

    package main import ( "fmt" "time" ) func recv(chn *chan int) { for { select { case v, ok := <-*chn: fmt.Println("value: ", v, "; ok: ", ok) if !ok { fmt.Println("channel closed!!! ") return // 退出协程 } default: if nil == *chn { fmt.Println("channel == nil"); return } fmt.Println("recv alive print.") time.Sleep(time.Second * 1) } } } func main() { var chn = make(chan int, 0) //var readOnlyChn = make(<-chan int) //var writeOnlyChn = make(chan<- int) /* 先启动接收协程, 在通道写入数据的时候进行读出, 由于之前创建了 0 缓存的通道, 写不同时读出造成堵塞 */ go recv(&chn) chn<-1024 // 写入1024数据 time.Sleep(time.Second * 2) // 延时两秒, 好让接收线程接收处理写入数据 chn = nil //close(chn) // 关闭通道 time.Sleep(5 * time.Second) }

    写超时处理一

    func main() { var chn = make(chan int, 0) // 创建 0 缓存通道 select { case chn<-1024: // 写数据 fmt.Println("Writed Done!!!") case <-time.After(time.Second * 3): // 三秒超时 fmt.Println("Time out") } }

    写超时处理(context.WithTimeout)二

    func main() { var chn = make(chan int, 0) // 创建 0 缓存通道 ctx, _ := context.WithTimeout(context.Background(), time.Second * 3) select { case chn<-1024: // 写数据 fmt.Println("Writed Done!!!") case <-ctx.Done(): // 三秒超时 fmt.Println("Done: ", ctx.Err()) } }

    下一篇介绍context

     

     

     

    Processed: 0.171, SQL: 9