在Golang函数中实例化goroutine的方法
在Golang函数中实例化goroutine的方法 你好,
我有一个需求,在函数中编写一个goroutine会更方便(即,它不是在主函数中创建的)。一旦函数退出,goroutine是否会继续按预期运行?这被认为是好的做法吗?到目前为止,我看到的唯一示例都只展示了在主线程/例程中实例化goroutine,我不确定这样做是强制要求,还是仅仅为了示例简洁。
一个看似有效的示例来演示:
package main
import (
"fmt"
"time"
)
func printForever() { // 我们的goroutine
x := 0
for {
fmt.Printf("hello again # %v\n", x)
x++
time.Sleep(1 * time.Second)
}
}
func doSomething() {
go printForever()
fmt.Println("all done here")
}
func main() {
doSomething() // 调用我的例程
for {
} // 永远停留在这里
}
更多关于在Golang函数中实例化goroutine的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
为什么 你启动新的 goroutine,或者 在那些 goroutine 中做什么,可能被认为是好的实践,也可能不是,但从 main 函数以外的函数启动 goroutine 本身没有任何问题或不寻常之处;这完全是正常的。
更多关于在Golang函数中实例化goroutine的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
为了澄清一下,让一个 goroutine 并发运行是可以的,即使调用并创建它的函数已经退出。对吗?我的示例程序就是这样工作的,目前一切正常…
感谢 @skillian,你的动作太快了,哈哈。我刚上传了一个我快速拼凑出来的可行示例。它执行得很好,但我不确定这种惯用风格的代码在 Go 中是否不受欢迎。我想你已经回答了我的问题,谢谢。
对于我的特定应用场景,是的,我希望这个 goroutine 能够无限期地运行。我之前曾使用布尔通道来实现优雅退出。我发现,直接退出主循环(例如按 CTRL+C 或退出一个用 Go 编写的 GUI 程序)会突然停止 Go 协程,这是可以预料的。利用通道在 goroutine 内部触发事件来运行 defer 代码等,效果很好。
我之前不知道 context.Context 这个选项,谢谢。我需要多读一些相关资料。
是的,在函数中启动goroutine是完全有效的,并且goroutine会继续运行,即使创建它的函数已经退出。只要主goroutine(或任何其他goroutine)仍在运行,程序就不会退出,所有已启动的goroutine都会继续执行。
在你的示例中,doSomething函数启动了一个goroutine,然后立即返回。由于主函数中有一个无限循环,程序不会退出,因此printForever goroutine会继续运行。这是一个常见的模式。
以下是一个更实际的示例,演示了在函数中启动goroutine,并使用通道(channel)进行通信和控制:
package main
import (
"fmt"
"time"
)
// worker是一个在后台运行的goroutine,它从jobs通道接收工作,并将结果发送到results通道。
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("worker %d started job %d\n", id, j)
time.Sleep(time.Second) // 模拟工作耗时
fmt.Printf("worker %d finished job %d\n", id, j)
results <- j * 2
}
}
// startWorkers是一个函数,它在内部启动多个goroutine(worker池)。
func startWorkers(numWorkers int, jobs <-chan int, results chan<- int) {
for w := 1; w <= numWorkers; w++ {
go worker(w, jobs, results)
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
// 在函数中启动worker池
startWorkers(3, jobs, results)
// 发送工作
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= numJobs; a++ {
<-results
}
}
在这个示例中,startWorkers函数启动了3个worker goroutine。即使startWorkers函数已经返回,这些worker goroutine仍然继续运行,处理从jobs通道接收的工作,直到通道被关闭。这是一种常见的并发模式,用于构建worker池。
因此,在函数中启动goroutine不仅是允许的,而且是Go语言中组织并发代码的推荐方式。关键是要确保你管理好goroutine的生命周期,避免goroutine泄漏(例如,使用上下文(context)取消、通道关闭或等待组(sync.WaitGroup)来协调goroutine的退出)。


