【青训营】浅谈goroutine

102 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第一篇笔记。

经过很多天的学习积淀后,我认为第一篇笔记应该总结一下goroutine和channel的用法。

第二讲中提到了并发编程中的四个要点

它们在工程实践中不只是“经常会用到”那么简单,应该说是“天天都在用”,非常重要;而且非常容易出bug,属于是go语言编程的难点。

goroutine和channel也恰是go语言的精华所在——快捷高效实现并发编程。下面我们就来看看goroutine与channel的用法。

goroutine快速入门

func main() {
   for i := 0; i < 1000; i++ {
      go func(i int) {
         for {
            fmt.Printf("Hello from "+
               "goroutine %d\n", i)
         }
      }(i)
   }
   time.Sleep(time.Minute)
}

此处开了1000个协程同时打印数字,此处让主程序睡眠1分钟,不然还没等协程打印主程序就退出了。这个方式显然不太优雅,而且协程调度大有问题,IO操作使得协程互相抢占,有一些数字顾及不到,根本打印不出来。

而且此处有一个问题,为什么非要让匿名函数接收这个i呢?如果不接收,程序就不安全。

package main

import (
   "fmt"
   "runtime"
   "time"
)

func main() {
   var a [10]int
   for i := 0; i < 10; i++ {
      go func() {
         for {
            a[i]++
            runtime.Gosched()
         }
      }()
   }
   time.Sleep(time.Millisecond)
   fmt.Println(a)
}

看下面一个程序,运行会报Index out of range的错误。这是因为goroutine开的是协程,上面在跑,下面也在跑。当主程序跳出for循环后,i = 10,而在协程中a[10]是不存在的,所以会有这个错误。

goroutine本质

  • goroutine是轻量级的线程
  • goroutine能够做到非抢占式多任务处理,由协程主动交出控制权
  • go语言是编译器/解释器/虚拟机层面的多线程
  • 多个协程可能在一个或多个线程上运行

goroutine可能的切换点

  • I/O, select(见第一个代码块)
  • channel
  • 函数调用(有时)
  • runtime.Gosched(见第二个代码块)
  • 等待锁

以上就是goroutine的注意事项,有什么疑问大家可以交流讨论