Go 语言 协程和管道讲解


当前第2页 返回上一页

在遍历时,若channel没有关闭,出现deadlock错误;在遍历时,若channel已经关闭,会正常遍历数据,遍历完之后,就会退出遍历; 

举个栗子: 

package main


import "fmt"


func main () {

    // 创建一个管道, 大小为200

    intChan := make(chan int, 200)

    for i := 0; i < 200; i++ {

        intChan<- i * 2

    }

    // 在遍历取值时, 一定要关闭管道

    close(intChan)

    // 遍历, 取出管道所有的值

    for value := range intChan{

        fmt.Println("value:", value)

    }

}

 

 

 如果在遍历取值的时候,不关闭管道会报错:fatal error: all goroutines are asleep - deadlock! 

 

2.5.协程与管道的使用: 

package main


import "fmt"


// 往管道里写入50条数据

func writeDate(intChan chan int)  {

    for i := 1; i <= 50; i++{

        // 写入数据

        intChan<- i

        fmt.Println("管道中写入数据:", i)

    }

    // 写完后,关闭此管道

    close(intChan)

}

// 从管道中读取数据

func readData(intChan chan int, exitChan chan bool) {

    for {

        v, ok := <-intChan

        // 说明intChan管道已经取完了

        if !ok{

            break

        }

        fmt.Printf("intChan 管道取出数据:%v\n", v)

    }

    // readData 取完后表示任务已经完成

    exitChan<- true

    close(exitChan)

}

func main()  {

    // 创建两个管道

    intChan := make(chan int, 50)

    // 退出管道, 主线程监控, 协程取完intChan后, 会写进此管道一条数据

    exitChan := make(chan bool, 1)


    // 开启写的协程、读的协程

    go writeDate(intChan)

    go readData(intChan, exitChan)


    // 写一个for循环, 监听exitChan管道, 若exitChan管道的数据取完, 主线程可以结束

    for {

        _, ok := <- exitChan

        if !ok{

            break

        }

    }

}

 

 

 切记,这里创建两个管道,是解决,主线程退出,协程还没有执行完,该程序就结束的问题;如果指向管道写入数据,而没有读取,就会出现阻塞dead lock,原因是超出了管道的容量; 

 

2.6.管道使用细节: 

 声明管道为只写: var chan2 chan<- int

chan2 = make(chan int, 3)

chan2<-20

  声明管道为只读: var chan3 <-chan int

num := <- chan3

  只读或只写,可以应用到函数传参时,做严格校验;  select可以解决从管道取数据阻塞问题: package main


import (

    "fmt"

    "time"

)


func main() {

    // 使用select 可以解决管道数据堵塞的问题

    // 1.定义一个int类型管道, 大小为10

    intChan := make(chan int, 10)

    for i := 0; i < 10; i++{

        intChan<- i

    }

    // 2.定义一个管道 5个数据string

    strChan := make(chan string, 5)

    for i := 0; i < 5; i++ {

        strChan<- "hello" + fmt.Sprintf("%s", i)

    }

    // 传统方法在遍历管到时, 如果不关闭会阻塞 导致deadlock

    // 实际开发中,有时不确定什么时候关闭该管道

    for {

        select {

            // 注意:若intChan 一直没有关闭, 不会一直阻塞而导致deadlock

            // 会自动到下一个case匹配

            case v := <- intChan:

                fmt.Printf("从intChan读取数据%d\n", v)

                time.Sleep(time.Second)

            case v := <- strChan:

                fmt.Printf("从strChan读取数据%s\n", v)

                time.Sleep(time.Second)

            default:

                fmt.Printf("取不到数据咯~~~\n")

                return

        }

    }

}

  协程中使用recover,解决协程中出现panic,导致程序崩溃问题: package main


import (

    "fmt"

    "time"

)


func sayGo() {

    // 写一个正常运行的函数

    for i := 0; i < 10; i++{

        time.Sleep(time.Second)

        fmt.Println("hello golang")

    }

}


func testErr() {

    // 使用defer + recover 进行对此函数的异常捕获

    defer func() {

        if err := recover(); err != nil{

            fmt.Printf("test()函数发生错误:%v", err)

        }

    }()


    // 写一个错误的函数

    var myMap map[int]string

    myMap[0] = "hello"

}

func main() {

    go sayGo()

    go testErr() // 若此函数错误, 不会影响到其余函数 所以在此函数里加上捕获异常


    for i := 0; i < 10; i++{

        fmt.Println("main() ok=", i)

        time.Sleep(time.Second)

    }

}



本文来自:51CTO博客

感谢作者:wx592a7561e9493

查看原文:Go 语言 协程和管道讲解

返回前面的内容

相关阅读 >>

leetcode131 分割回文串 Golang

Go语言入门

Go语言运算符

Golang出现panic是什么原因

总结air在Go的其他版本上运行可能遇到的问题

Golang如何实现简单的api网关

Go语言标准库之fmt

关于Golang当中对select的理解

Golang websocket失败怎么办

Golang map需要make吗

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...