golang goroutine

使用

  • goroutine无法设置优先级,无法获取ID,没有局部储存,返回值被忽略

    • 通过channel传递结果
  • 通过设置runtime.GOMAXPROCS(n)可以控制真正的并行度

    • 默认值为CPU核数:runtime.GOMAXPROCS(runtime.NumCPU())
  • 可以利用runtime.Gosched()让协程轮流执行

    • runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine
    • 该函数很少被使用,因为无法保证调度总是成功
  • defer一样, goroutine也会对传入的参数立刻进行计算

var c int
func counter() {
    c++
    return c
}   

go func(x){
    counter()
    fmt.Print(x) // 打印出0,而不是1
}(counter())
  • 并行for循环
    • 注意i, xi需要通过参数传入而不能当成闭包的环境,否则会出错
      • 也可以在for循环中赋值给新建的局部变量解决
    • 原因在于i, xi在循环的过程中地址不变
for i, xi := range data {
	go func (i int, xi float64) {
		res[i] = doSomething(i, xi)
		sem <- empty
	} (i, xi)
}
// wait for goroutines to finish
for i := 0; i < N; i++ { <-sem }
  • 多个goroutine协调工作涉及 通信,同步,通知,退出 四个方面
    • 通信:chan通道是各goroutine之间通信的基础。注意这里的通信主要指程序的数据通道。
    • 同步:可以使用不带缓冲的chan;sync.WaitGroup为多个gorouting提供同步等待机制;mutex锁与读写锁机制。
    • 通知:通知与上文通信的区别是,通知的作用为管理,控制流数据。一般的解决方法是在输入端绑定两个chan,通过select收敛处理。这个方案可以解决简单的问题,但不是一个通用的解决方案。
    • 退出:简单的解决方案与通知类似,即增加一个单独的通道,借助chan和select的广播机制(close chan to broadcast)实现退出

原理

  • goroutine采用M:N模型,在所有线程上进行多路复用

    • 当一个goroutine被阻塞时,所在的线程也被阻塞,而go的runtime会把该线程上的其他协程移动到未阻塞的线程
      • 已存在的 goroutine 总是会被优先复用
    • 每个goroutine拥有自己的寄存器上下文和栈
    • 优点
      • 控制了系统线程的数量
        • 保证每个线程一定的运行时间
        • 避免线程过多而导致调度开销过大
      • 程序可以在用户态自行调度
        • 避免了单个协程阻塞了整个线程的情况、
        • 避免了大量内核态和用户态切换
      • 更小的栈空间占用
        • 允许用户创建成千上万的实例
  • 调度方式

    • 在runtime中实现了一个调度器, 一旦进行了Golang库函数调用, runtime就有机会进行调度
      • 如果写了死循环就没法触发goroutine的调度
    • 操作系统会在物理处理器上调度线程来运行,而Go 语言的运行时会在逻辑处理器上调度
      goroutine来运行
    • 调度器把goroutine调度到的CPU不一定是程序进程所在的CPU

golang goroutine_第1张图片golang goroutine_第2张图片
golang goroutine_第3张图片

参考

  • 图解goroutine调度
  • 一个内部API系统的性能优化

你可能感兴趣的:(Golang)