美文网首页
28.Go语言·协程Goroutine·管道Channel(二)

28.Go语言·协程Goroutine·管道Channel(二)

作者: 一枼落知天下 | 来源:发表于2019-06-12 12:37 被阅读0次

    main.go

    // Go语言·协程Goroutine·管道Channel
    package main
    
    
    import (
        // model "day32/model"
        "fmt"
        "time"
    )
    
    var content string = `
    ————————————————Go语言·协程Goroutine·管道Channel————————————————————
    一、协程&管道双剑合璧
        1.死锁,管道阻塞
            编译器,发现一个管道只有写,没有读,则管道,会阻塞
            不停的写入管道,却没有从管道中读取。
                1.写的地方报错:deadlock
                2.读的地方报错:deadlock
            写管道和读管道的频率不一致,无所谓,有效阻塞。
    二、求素数(1-300000)
        思路分析:
            model.PrimeCount()
            素数:299993
            主线程退出~~~完成!耗时: 4 秒
    三、使用细节和注意事项
        1.管道可以声明只读或只写
            // 只写
            var chan2 chan<- int
            // 只读
            var chan3 <-chan int
            应用场景:   
                协程参数只读只写
                协程参数,可以声明为只写,只读
        2.在默认情况下,管道是双向的。
        3.select可以解决从管道取数据的阻塞问题
            select {
                case v:=<-intChan:
                    fmt.Println("intChan数据:",v)
                    time.Sleep(time.Second) 
                case v:=<-stringChan:
                    fmt.Println("stringChan数据:",v)
                    time.Sleep(time.Second)
                default:
                    fmt.Println("什么也么有!!")
                    time.Sleep(time.Second)
                    return
            }
        4.协程中使用recover,解决协程中出现panic,解决程序崩溃
            // defer +recover
            defer func() {
                if err:=recover();err != nil {
                    // 记录日志,发消息通知管理员等操作。
                    fmt.Println("程序报错咯:",err) 
                }
            }()
    `
    
    
    
    func main() {
        
        go sayHello()
    
        go testMap()
    
        for i := 0; i < 10; i++ {
            time.Sleep(time.Second)
            fmt.Println("main() :OK !")
        }
    
    }
    
    func test() {
        // 只写
        var chan2 chan<- int
        chan2 = make(chan int, 3)
        chan2<-2
        // num := <-chan2
        // send-only type chan<- int
        // fmt.Println(chan2)
        // 只读
        var chan3 <-chan int
        // chan3<-6
        // receive-only type <-chan int
        <-chan3
    }
    
    func testSelect(){
        intChan := make(chan int, 10)
        for i := 0; i <10; i++ {
            intChan<-i
        }
    
        stringChan := make(chan string ,5)
        for i := 0; i <5; i++ {
            stringChan<-"hello"+fmt.Sprintf("%d",i)
        }
    
        // 传统的方法在遍历的管道时,如果不关闭会阻塞而导致 deadlock
        // 在实际开发中,我们可能不确定在什么时候关闭管道
        // 可以用select方式解决
        for {
            select {
                case v:=<-intChan:
                    fmt.Println("intChan数据:",v)
                    time.Sleep(time.Second) 
                case v:=<-stringChan:
                    fmt.Println("stringChan数据:",v)
                    time.Sleep(time.Second)
                default:
                    fmt.Println("什么也么有!!")
                    time.Sleep(time.Second)
                    return
            }
        }
    }
    
    
    func sayHello() {
        for i := 0; i < 10; i++ {
            time.Sleep(time.Second)
            fmt.Println("Hello World !")
        }
    }
    
    
    func testMap() {
        // defer +recover
        defer func() {
            if err:=recover();err != nil {
                fmt.Println("程序报错咯:",err) 
            }
        }()
        var myMap map[int]string
        myMap[0] = "golang"
    }
    

    model/Single.go

    package model
    
    import (
        "fmt"
        "time"
    )
    
    /**
     * [PrimeCount 求素数(1-300000)]
     * @author Jhou Shuai
     */
    func PrimeCount() {
        start := time.Now().Unix()
        // 协程数
        var counts int = 8
        var num int = 300000
    
        intChan := make(chan int, 10000)
    
        primeChan := make(chan int, 20000)
    
        done := make(chan bool, counts)
    
        go putNum(intChan, num)
    
        for i := 0; i < counts; i++ {
            go getPrime(intChan, primeChan, done)
        }
    
        // 主线程  关闭管道
        go func() {
            for i := 0; i < counts; i++ {
                <-done
            }
            close(primeChan)
        }()
    
        // 遍历结果primeChan管道,打印输出
        for {
            res, ok := <-primeChan
            if !ok {
                break
            }
            fmt.Printf("素数:%v \n", res)
        }
        end := time.Now().Unix()
        fmt.Println("主线程退出~~~完成!耗时:", end-start, "秒")
    }
    
    // 向管道中写入 300000个数
    func putNum(intChan chan int, num int) {
        for i := 1; i <= num; i++ {
            intChan <- i
        }
        // 关闭管道
        close(intChan)
    }
    
    func getPrime(intChan chan int, primeChan chan int, done chan bool) {
        // 素数
        var flag bool
        for {
            num, ok := <-intChan
            if !ok {
                // 管道中取不到数据咯。退出
                break
            }
            flag = true
            for i := 2; i < num; i++ {
                if num%i == 0 {
                    // 不是素数,跳出循环
                    flag = false
                    break
                }
            }
    
            if flag {
                primeChan <- num
            }
        }
        // 当前协程工作完毕
        done <- true
    }
    

    相关文章

      网友评论

          本文标题:28.Go语言·协程Goroutine·管道Channel(二)

          本文链接:https://www.haomeiwen.com/subject/ddnkfctx.html