Go语言并发之道学习笔记(一)
写在前面的话: 近期一直在学Go语言,并发是必不可少的,看《Go语言并发之道》做个记录,方便回顾,里面很多内容也不完善,希望有所收获
1. 基本概念
- 并发和并行的区别
- 并发属于代码,并行属于一个运行中的程序。
- 我的理解是并行是指同时刻运行同一段代码(这段代码实现的功能是同时生效的),并发时同时刻可以运行不同的代码(可以是不同功能的代码)
- sync 和 channel 的区别
- sync 对性能要求高,保护某个结构的内部状态,不关心操作的结果
- channel 需要转让数据的所有权,协调多个逻辑判断
- 尽量使用 channel
2. goroutine 是否改变闭包内变量的值
先来看一段代码,输出是什么?
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
salutation := "hello"
wg.Add(1)
go func() {
defer wg.Done()
salutation = "welcome"
}()
wg.Wait()
fmt.Println(salutation)
}
- 输出结果:
welcome
- 结论:
goroutine在他们所创建的相同地址空间内执行
我们更改一下main部分
func main() {
for _, salutation := range []string{"hello", "greetings", "good day"} {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(salutation)
}()
}
wg.Wait()
}
-
输出结果:
good day
good day
good day -
结论:
goroutine 在运行一个闭包,在闭包使用变量 salutation 时,字符串的迭代已经结束。goroutine 开始之前循环又很高的概率会退出,意味着变量 salutation 的值不在范围内。所以 salutation 会被转移到堆中。
若要循环打出,正确方式如下:
- 声明参数,将变量显示的映射到闭包中
- 将当前迭代的变量传递给闭包,创建了一个字符串结构的副本,从而确保当 goroutine 运行时,我们可以引用适当的字符串
for _, salutation := range []string{"hello", "greetings", "good day"} {
wg.Add(1)
go func(s string) {
defer wg.Done()
fmt.Println(s)
}(salutation)
}
wg.Wait()