1.1.1. 一、使用channel (管道)的注意事项:

1、channel 可以声明为只读,或者只写。

默认情况下管道是双向的,但是管道声明的时候,可以声明为只读,或者只写。

  • 声明只写管道:var intChan chan<- int
func main() {
    //只写管道 chan<-
    var intChan chan<- int  = make(chan int,3)
    intChan<- 10
    intChan<-20

    //<-intChan //编译报错,只写管道,不能读取。
}
  • 声明只读管道:var intChan <-chan int
func main() {
    //只读管道 <-chan
    var intChan <-chan int  = make(chan int,3)
    //intChan<- 10 编译错误,只读管道,不能写。
}

还可以把只读,或只写的管道作为函数参数。

//只写管道intChan
//只写管道 readChan
func test(intChan chan<- int,readChan <-chan int){

}

2、使用 select 可以解决从管道取数据的阻塞问题。

传统的方法(for-range)在遍历管道取数据时,如果管道没有关闭,会导致死锁。传统方法在取没有关闭的管道的数据时,如果没有数据,再取,也会报错死锁。使用 select 可以解决这个问题。

  • 使用select从管道取数据的语法:
        select {
            case v := <-intChan:
                fmt.Printf("从 intChan 读取的数据%d \n", v)
            case v := <-stringChan:
                fmt.Printf("从 stringChan 读取的数据%v \n", v)
            default:
                fmt.Println("没有读取到数据。。。")
        }

case 后面取管道的数据,即使管道一直不关闭,也不会一直阻塞,就不会导致死锁。

因为 第一个case取不到,执行下一个case,如果全部都没有,就执行default。

  • 完整的例子如下:
    // 声明管道intChan,并放入数据
    intChan := make(chan int,3)
    for i:=0; i<3; i++  {
        intChan<- i
    }

    // 声明管道stringChan,并放入数据
    stringChan := make(chan string,3)
    for i:=0; i<3; i++  {
        stringChan<- strconv.Itoa(i) + "stringChan"
    }

    // for循环取管道的数据。取数据的时候使用select 
    label:
    for  {

        select {
            case v := <-intChan:  //管道一直不关闭,也不会一直阻塞,就不会导致死锁。
                fmt.Printf("从 intChan 读取的数据%d \n", v)
            case v := <-stringChan:
                fmt.Printf("从 stringChan 读取的数据%v \n", v)
            default:
                fmt.Println("没有读取到数据。。。")
                break label //跳到label,结束for循环
        }

    }

3、goroutine 中使用 recover,解决协程中出现 panic,导致程序崩溃问题。

如果开启了一个协程,但是这个协程中出现了panic错误。如果我们不捕获这个panic错误,就会造成整个程序崩溃。

这时候我们可以在goroutine 中使用 recover来捕获panic,进行处理。这样即使协程发生问题,但是主线程不受影响,可以继续执行下去。

// 正常代码
func test()  {
    for i := 0; i < 3; i++ {
        fmt.Println("hello..",i)
    }
}

// 捕获异常代码
func testErr()  {

    // defer 延迟执行
    defer func() {
        // 捕获异常
        if err := recover(); err != nil{
            fmt.Println("testErr() 发生错误", err)
        }
    }()

    var myMap map[int]string
    myMap[0] = "golang" //error,没有make分配空间

}

func main() {

    go test()
    go testErr()

    time.Sleep(time.Second * 2)
    fmt.Println("main 结束。。。。。。")

}

执行结果如下:

hello.. 0
hello.. 1
hello.. 2
testErr() 发生错误 assignment to entry in nil map
main 结束。。。。。。

results matching ""

    No results matching ""