Go基础知识梳理(四)

Go基础知识梳理(四)

GO的哲学是“不要通过共享内存来通信,而是通过通信来共享内存”,通道是GO通过通信来共享内存的载体。

rumtime包常用方法

runtime.NumGoroutine() //返回当前程序的协程数量
runtime.GOMAXPROCS(0) //获取当前的GOMAXPROCS数量
runtime.GOMAXPROCS(2) //设置程序的GOMAXPROCS数量为2

goroutine

特性

  • 执行是非阻塞的,不会等待
  • go 后面的返回值会被忽略
  • 调度器不能保证goroutine的执行顺序
fun main() {
    //另起一个协程去打印
    go func() {
        fmt.Println("goruntine") 
    }()
}

chan

//创建chan
c := make(chan dataType)
//无缓冲, len 和 cap 都是0
fmt.Println(len(c)) //0
fmt.Println(cap(c)) //0


//有缓冲
c = make(chan dataType, 10)
//len 代表没有被读取的元素数, cap 代表整个通道的容量
fmt.Println(len(c)) //0
fmt.Println(cap(c)) //10

WaitingGroup组件

Add(10) 给内置计数器加10
Done() 相当于Add(-1)
Wait() 内置计数器不为0则一直等待

// 写法
func main() {
    var wg sync.WaitGroup
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func(i int) {
            defer wg.Done()
            fmt.Println(i)
            time.Sleep(time.Millisecond * 3000)
        }(i)
    }
    wg.Wait()
    fmt.Println("done")
}

select

  1. 先打乱所有的case语句
  2. 遍历case语句,查看是否已经读写了
  3. 选择可读写的case去执行
  4. 没有可读写的case,则查看default语句是否定义,再去执行
  5. 没有defaul语句,则会等待可执行的case

看一段融合了并发,缓冲,退出通知等多重特性的代码

func GenerateA(done chan struct{}) chan int {
    ch := make(chan int, 5)
    // fmt.Println("通道数+1")
    go func() {
        fmt.Println("线程数+A")
    Label:
        for {
            select {
            case res := <-done:
                fmt.Println("done", res)
                break Label
            case ch <- rand.Int():
            }

        }
        close(ch)
    }()
    return ch
}
func GenerateB(done chan struct{}) chan int {
    ch := make(chan int, 5)
    go func() {
        fmt.Println("线程数+B")
    Label:
        for {
            select {
            case res := <-done:
                fmt.Println("done", res)
                break Label
            case ch <- rand.Int():
            }
        }
        close(ch)
    }()
    return ch
}

func GenerateInt(done chan struct{}) chan int {
    //无缓冲通道
    ch := make(chan int)
    send := make(chan struct{})
    go func() {
    Label:
        for {

            select {
            case <-done:
                send <- struct{}{}
                send <- struct{}{}
                break Label
            case ch <- <-GenerateA(send):
                fmt.Println("chose A")
            case ch <- <-GenerateB(send):
                fmt.Println("chose B")
            }
        }
        close(ch)
    }()
    return ch
}

func main() {
    done := make(chan struct{})
    ch := GenerateInt(done)
    fmt.Println(runtime.NumGoroutine())
    for i := 0; i < 10; i++ {
        fmt.Println(<-ch)
    }
    fmt.Println(runtime.NumGoroutine())
    done <- struct{}{}
    fmt.Println("stop gernate", struct{}{})
    time.Sleep(1 * time.Second) //等待1s,让停止的goruntime打印,如果不加这句话,可能会导致主线程比新起的协程早退出,从而无法打印出done {}
}

打印结果不展示,其中发现的问题:
1.打印出来的数字长度不固定
2.每次Select查看case是否堵塞的时候,都会执行一次该方法

Context

func main() {
    ctxa, cancel := context.WithCancel(context.Background())
    go work(ctxa, "work1")
    tm := time.Now().Add(3 * time.Second)
    ctxb, _ := context.WithDeadline(ctxa, tm)
    go work(ctxb, "work2")
    oc := OtherContext{ctxb}
    ctxc := context.WithValue(oc, "key", "andes, pass from main")
    go workWithValue(ctxc, "work3")
    time.Sleep(10 * time.Second)
    cancel()
    time.Sleep(5 * time.Second)
    fmt.Println("main stop")

}

type OtherContext struct {
    context.Context
}

func work(ctx context.Context, name string) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("%s get msg to cancel\n", name)
            return
        default:
            fmt.Printf("%s is running \n", name)
            time.Sleep(1 * time.Second)
        }
    }
}

func workWithValue(ctx context.Context, name string) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("%s get msg to cancel\n", name)
            return
        default:
            value := ctx.Value("key").(string)
            fmt.Printf("%s is running value=%s \n", name, value)
            time.Sleep(1 * time.Second)
        }
    }
}

这段代码中,都是一条链路下来的
根节点 context.Background() -> ctxa -> ctxb -> oc
其中ctxa加了个时间控制,所以到达一定的时间就会自动关闭
oc附带了个键对值

上一篇:【Go语言入门教程】Go语言容器(container)


下一篇:java web-01-servlet