Go 语言的 goroutine

Go 语言的 goroutine

Go 语言中,主 goroutine 中的代码执行完毕,当前程序就会立刻结束运行,无论其他 goroutine 是否正在运行。

package main

import "fmt"

func main() {
  for i := 0; i < 10; i++ {
    go func() {
      fmt.Println(i)
    }()
  }
}

执行上面的代码,会发现在绝大部分情况下,是什么都没有输出的。

其他 goroutine 还没有执行到 fmt.Println(i) 时,主 goroutine 中的代码就执行完毕了,让当前程序立刻结束运行。

如何让其他 goroutine 先执行完呢?最简单的方法,是让主 goroutine 等待一下。

package main

import (
  "fmt"
  "time"
)

func main() {
  for i := 0; i < 10; i++ {
    go func() {
      fmt.Println(i)
    }()
  }

  time.Sleep(time.Millisecond * 500)
}

没有输出的问题解决了,但是,等待多久是合适的呢?

这种情况,Sleep 是一个不太好的实现,我们应该用 sync.WaitGroup

package main

import (
  "fmt"
  "sync"
)

var waitgroup sync.WaitGroup

func main() {
  for i := 0; i < 10; i++ {
    waitgroup.Add(1)
    go func() {
      fmt.Println(i)
      waitgroup.Done()
    }()
  }

  waitgroup.Wait()
}

我们看到 sync.WaitGroup 解决了等待时间不确定的问题,但是打印出来全是 10,执行顺序不确定线程也不安全。

那么问题来了,如何让多个 goroutine 按顺序执行并且线程安全?

一般来说,我们使用锁解决这种问题,而自旋锁就是一种具体的实现。

package main

import (
  "fmt"
  "sync"
)

var mu sync.Mutex

func main() {
  for i := 0; i < 10; i++ {
    mu.Lock()
    fun := func() {
      fmt.Println(i)
      defer mu.Unlock()
    }

    go fun()
  }
}

GO 语言中,通道是比锁更简单的实现。

package main

import (
  "fmt"
)

func main() {
  message := make(chan int)

  for i := 0; i < 10; i++ {
    go func() {
      message <- i
    }()

    fmt.Println(<-message)
  }
}

通道和锁

通道的能力是让数据流动起来,擅长的是数据流动的场景。

  • 传递数据的所有权,即把某个数据发送给其他协程
  • 分发任务,每个任务都是一个数据
  • 交流异步结果,结果是一个数据

锁的能力是数据不动,某段时间只给一个协程访问数据的权限,擅长数据位置固定的场景。

  • 缓存
  • 状态

本篇
Go 语言的 goroutine Go 语言的 goroutine
Go 语言的 goroutineGo 语言中,主 goroutine 中的代码执行完毕,当前程序就会立刻结束运行,无论其他 goroutine 是否正在运行。 package main import "fmt" func main()
2019-03-28
下一篇
浅谈 Go 语言作用域 浅谈 Go 语言作用域
浅谈 Go 语言作用域package main import "fmt" var block = "package" func main() { block := "function" { // := 会声明一个新变
2019-03-13