陈大剩博客

Golang 并发编程(二): Goroutine

  • 陈大剩
  • 2025-01-22 00:49:25
  • 76

golang 标题

Goroutine

说到 goroutine (或称 G)就不得不提到 Go 语言特有的关键字 go,它是用户程序启用 goroutine 的唯一途径,一条 go 语句意味着一个方法或函数的并发执行操作。例如:

go fmt.Println("hello world")

go 语句是由 go 关键字(请区分:关键字、函数)和表达式组成的,表达式就是描述针对若干操作数的计算方法的式子,表达式有很多种,其中包括调用表达式,调用表达式是针对函数或方法的调用(函数可以是命名的也可以是匿名的)。

// 匿名 goroutine
go func() {
    fmt.Println("hello world")
}()

// 命名 goroutine
go funcName()

注意:goroutine 中包含主 goroutine 和 用户 goroutine。封装 main 函数的 goroutine 称为主 goroutine,主 goroutine 负责管理整个生命运行周期,这里我们只介绍用户 goroutine,主 goroutine 具体可以阅读源码。

使用

首先使用上列举几个并发常见的问题。

例1:主程序退出导致无法输出信息

如果使用 go 命令运行如下代码:

func main() {
    go fmt.Println("hello world")
}

这将不会输出 “hello world” ,这是因为 主程序退出,在调用 go fmt.Println("hello world") 后,主程序没有任何阻塞或等待的代码,导致 main 函数在启动 goroutine 后立即返回并退出。由于主程序退出,所有的 goroutine 也被终止,即使它们还没有执行完。

func main() {
    go fmt.Println("hello world")
    time.Sleep(time.Second)
}

这里作用是用它的 goroutine 暂停一段时间,理想状态下,运行可以看到输出。但是,真实情况并不总是这样(因为调度器我们无法控制)。

例2:循环作用域变化无法输出正确信息

如果 golang 版本为 1.22 以下输出 name 时大概率只会出现 “wangwu”、”wangwu”、”wangwu”….

names := []string{"dasheng", "zhangsan", "lisi", "wangwu"}
for _, name := range names {
    go func() {
       fmt.Println(name)
    }()
}
time.Sleep(time.Second)

这是因为循环作用域导致无法输出正确信息, golang 1.22 版本以上修复了此问题,但是正常解因使用传参进行,保持规范。

names := []string{"dasheng", "zhangsan", "lisi", "wangwu"}
for _, name := range names {
    go func(s string) {
       fmt.Println(s)
    }(name)
}
time.Sleep(time.Second)

第二种代码不出错的原因是,name 变量的类型 string 是一个非引用类型,把一个值作为参数传给函数的时候,该值会被复制。但对于引用类型(切片、字典)的值来说,由于它类似于指向真正数据的指针,所以即便被复制了,外部修改也会反应到函数内部,所以使用引用类型无法使用传值

题外话

我用这段代码去问面试候选人,没有几个答对的,你们说是为什么?

names := []string{"dasheng", "zhangsan", "lisi", "wangwu"}
for _, name := range names {
    go func(s string) {
       fmt.Println(s)
    }(name)
}
time.Sleep(time.Second)
分享到:
0

说点儿什么吧

头像

表情

本站由陈大剩博客程序搭建 | 湘ICP备2023000975号| Copyright © 2017 - 陈大剩博客 | 本站采用创作共用版权:CC BY-NC 4.0

站长统计| 文章总数[123]| 评论总数[11]| 登录用户[26]| 时间点[127]

logo

登入

社交账号登录