Go中的goroutine

268 阅读3分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

Goroutine

一种运行在用户态下的轻量级的线程, 称为微线程或协程.

与线程和进程相比, 协程的执行效率高. 无线程和进程切换的开销.

在Go程序启动的时候, main函数也是运行在一个goroutine中, 称为main goroutine

当使用用go来调用一个函数时, 都会再创建一个新的goroutine

goroutine的创建

 func sayHi()  {
   for i := 0; i < 100; i++ {
     fmt.Println("我是一个goroutine, ", i)
   }
 }
 ​
 // 在函数前面加上关键字: go, 现在就执行了一个goroutine
 // 一个goroutine必定对应一个函数, 多个goroutine可以执行相同的函数
 // 至此为止, 我们已经学完了
 go sayHi()
 fmt.Println("我是main函数 , 我执行完了")
 ​
 // 当main函数执行完毕之后, 未执行完的goroutine也会停止执行
 // 比如像这样, 当能在sayHi执行到一半的时候, main函数就执行完了
 // 而sayHi函数的执行也会退出
 // 我们可以选择让main goroutine晚退出一会, 来让其它goroutine执行完毕
 time.Sleep(time.Microsecond * 1000)

sync.WaitGroup

 // 创建一个全程的waitGroup
 var wg sync.WaitGroup
 ​
 func sayHi()  {
    fmt.Println("我是sayHi函数, 我开始执行了")
    for i := 0; i < 100; i++ {
       fmt.Println("我是一个goroutine, ", i)
    }
   // 每当程序执行完毕要退出的时候, 调用Done方法
   // 表示有一个goroutine执行完毕
    wg.Done()
 }
 ​
 // 每当有一个goroutine要开始执行时, 调用Add函数, 进行加1操作
 // 表示, waitGroup里又多了一个goroutine在在执行
 wg.Add(1)
 go sayHi()
 ​
 // 当waitGroup里没有在执行的goroutine时, 会调用这个方法
 wg.Wait()
 fmt.Println("我是main函数 , 我执行完了")

开启多个goroutine

 var wg sync.WaitGroup
 var count int
 ​
 func sayHello(i int)  {
   count++
   fmt.Println("我是函数sayHello, 我是一个goroutine, ", i)
   wg.Done()
 }
 ​
 wg.Add(20)
 for i := 0; i < 20; i++ {
   go sayHello(i)
 }
 wg.Wait()
 fmt.Println("执行了sayHello: ", count, "次")
 fmt.Println("我是main函数 , 我执行完了")

goroutine匿名函数

 /*
 wg.Add(20)
 for i := 0; i < 20; i++ {
   // 匿名函数捕获外部变量
    go func() {
      // 打印i的时候, 会出现多个重复的值
       fmt.Println("我是一个匿名函数: ", i)
       wg.Done()
    }()
 }
 */
 ​
 wg.Add(20)
 for i := 0; i < 20; i++ {
   // 我们应该使用传参的方式, 来获取外部变量的值
   go func(i int) {
     fmt.Println("我是一个匿名函数: ", i)
     wg.Done()
   }(i)
 }
 ​
 wg.Wait()
 fmt.Println("我是main函数 , 我执行完了")

GOMAXPROCS

 func func1()  {
   for i := 0; i < 100; i++ {
     fmt.Println("我是func1, ", i)
   }
 }
 ​
 func func2()  {
   for i := 0; i < 100; i++ {
     fmt.Println("我是func2, ", i)
   }
 }
 ​
 func func3()  {
   for i := 0; i < 100; i++ {
     fmt.Println("我是func3, ", i)
   }
 }
 ​
 // 指定使用多少个系统态线程来执行goroutine
 // 当设置只有一个CPU来执行goroutine时, 我们可以看到一般情况下, 都是执行完一个goroutine, 再执行另一个
 // 当有多个CPU来执行goroutine时, 我们可以看到多个goroutine交换的在控制台打印输出
 runtime.GOMAXPROCS(3)
 go func1()
 go func2()
 go func3()
 fmt.Println("我是main函数, 我完了")
 time.Sleep(time.Second)

总结

goroutine是一个轻量级的线程, goroutine的管理是由go实现的调度器来统一管理的.

goroutine和系统中的线程是多对多的关系, 并且我们不需要关心它们是如何调度的.

可以把更多的精力放在业务功能的编写上, 嗯, Golang天生支持并发编程!!!