【问题标题】:Why aren't my goroutines launched?为什么我的 goroutine 没有启动?
【发布时间】:2016-03-05 05:07:48
【问题描述】:

不,不是因为我的程序结束得太快了。

我有这个脚本:

package main

import ("log"; "io/ioutil"; "strings")

const BASE_FILE_NAME = "abc_"

func mygoroutine(file_name string) {
    log.Println("In goroutine for file", file_name)
}


func get_file_names() []string {
  file_names := make([]string, 0)
  files, _ := ioutil.ReadDir("./")
  for _, file := range files {
      if strings.HasPrefix(file.Name(), BASE_FILE_NAME) {
        file_names = append(file_names, file.Name())
      }
  }

  return file_names
}

func main()  {
    file_names := get_file_names()
    for _, file_name := range file_names {
        log.Println("Now lunching goroutine for file", file_name)
        go mygoroutine(file_name)
    }

    log.Println("Finished launching.")

    for {}

    log.Println("Now exiting")
}

在包含可执行文件的目录中,我有两个以abc_ 开头的文件,所以输出是这样的:

2016/03/04 20:35:14 Now lunching goroutine for file abc_fr
2016/03/04 20:35:14 Now lunching goroutine for file abc_hrty
2016/03/04 20:35:14 Finished launching.

脚本不会停止,它永远不会记录 Now exiting ,因为它在空的 for 中循环。但我没有看到In goroutine for file 消息。

为什么会这样?我做错了什么?

感谢您的帮助!

【问题讨论】:

  • 如果 GOMAXPROCS=1, for{} 冻结你的程序,而不会让它切换 goroutines。 select{} 不这样做也不吃 CPU。
  • 您可以使用 WaitGroup 代替 for {} 循环 - 请参阅 nathanleclaire.com/blog/2014/02/15/…
  • 是的,我认为你是对的。在看到您的评论之前,我用 time.Sleep() 更改了 for {} 并且它起作用了。我现在将更改 GOMAXPROCS,看看会发生什么。
  • 实际上,即使 GOMAXPROCS>1 for{} 最终会在 GC 尝试停止世界时完全阻塞调度程序。永远不要使用繁忙的循环。
  • 查看这个答案stackoverflow.com/a/34885000/2363866 也许谷歌关于 go scheduler,有一些很棒的博客文章和 golang 团队设计文档。其实并不像busy loop freezes your program那么简单。它在某些情况下可能会冻结,在其他情况下,即使使用 GOMAXPROCS=1 并且没有阻塞操作,它也可以工作。

标签: go goroutine


【解决方案1】:

如果您的程序使用GOMAXPROCS=1(即单个操作系统线程)运行,for{} 将冻结它而不让 Go 的用户模式调度程序运行。 This is the issue about it。 JimB 指出它会导致其他问题,无论GOMAXPROCS;最终运行时必须停止你的 goroutine 进行垃圾收集,它不能停止 for{}

for{} 更改为select{} 可以让调度程序运行并且不会占用CPU。 In this reduced program, your goroutine code runs。它以“所有 goroutines 都睡着了——死锁!”结尾。因为你的另一个 goroutine 退出了,唯一剩下的一个(主)挂在 select{} 中。

package main

import "log"

const BASE_FILE_NAME = "abc_"

func mygoroutine(file_name string) {
    log.Println("In goroutine for file", file_name)
}

func main() {
    go mygoroutine("foo")
    log.Println("Finished launching.")
    select {}
    log.Println("Now exiting")
}

当然,即使使用select{},您通常也不想挂起 goroutine;这将在您的程序结束之前留下一些资源在使用中。要构建有用的东西,您需要其他东西,例如 sync.WaitGroup 或频道。

【讨论】:

  • @RandomGuyqwert - 请接受这个答案,以便其他人可以看到有效的解决方案
猜你喜欢
  • 2014-08-17
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
  • 2015-08-23
相关资源
最近更新 更多