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

2019-06-12  本文已影响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
}
上一篇 下一篇

猜你喜欢

热点阅读