一、介绍
var a int
a := 0
二、for循环go协程作用域
1.我们先看下最简单的for如下
package mainimport "fmt"func main() {
for i := 0;
i < 2;
i++ {
go func() {
fmt.Println(i)
}()
}
}
【go for循环中的作用域】上面的代码,最后没有输出。我们的期望输出 0 1 的愿望落空了,什么原因?
程序执行时间图
文章图片
如上图,我们发现在上面的程序有2种情况
1.当主进程结束,goroutine2,goroutine3 都还没执行完,则没有输出。
2.情况2,如下图 goroutine2 执行完,输出了1 !!!!注意不是0!!!!
文章图片
从上面学到的解决竞态4中方案,我们用waitGroup阻塞主进程,改进代码如下:
package mainimport (
"fmt"
"sync"
)func main() {
wg := sync.WaitGroup{}
for i := 0;
i < 2;
i++ {
wg.Add(1)
go func() {
fmt.Println(i)
wg.Done()
}()
}
wg.Wait()
}
输出如下:
2
2
终于两个goroutine都执行,但结果和我们其他的0,1 完全不一样。为啥?
文章图片
当goroutine2,goroutine3 打印的时候,main goroutine 已经将 i变成2了,而在整个for内,i是同一个变量。
此时就打印变成了2了。
2.for循环中go协程作用域
我们看下面的示例代码
package mainimport (
"fmt"
"time"
)func main() {
// for 循环作用域开始
for i := 0;
i < 2;
i++ {
go func() {
fmt.Println(i)
}()
}//for 循环作用域结束
time.Sleep(time.Second)
}
输出结果如下:
2
2
这是因为 i 的作用域的原因,我们可以把for循环的代码重写如下:
func main() {
// for 循环作用域开始
var i int
for i = 0;
i < 2;
i++ {
go func() {
fmt.Println(i)
}()
}//for 循环作用域结束
time.Sleep(time.Second)
}
这两种写法是等价,可以看出来 i 的作用域 为第2行到第7行,括号结束。此时就会出现上面的都是2的情况。
那么我们只要把传进goroutine的变量范围变成goroutine之内就能解决这个问题了。
2.1 方法一goroutine加参数
func main() {
var i int
for i = 0;
i < 2;
i++ {
go func(j int) {
fmt.Println(j)
}(i)
}
time.Sleep(time.Second)
}
输出如下:
0
1
2.2方法二缩小变量范围到goroutine的范围内
func main() {
var i int
for i = 0;
i < 2;
i++ {
j := i
go func() {
fmt.Println(j)
}()
}
time.Sleep(time.Second)
}
3.变量范围改造代码改造如下
package mainimport (
"fmt"
"sync"
)func main() {
wg := sync.WaitGroup{}
for i := 0;
i < 2;
i++ {
wg.Add(1)
go func(j int) {
fmt.Println(j)
wg.Done()
}(i)
}
wg.Wait()
}
输出
1
0
执行如下,此次是 goroutine3 先打印了,然后才是 goroutine2
文章图片
说明 for goroutine 中的执行顺序是 无序的 ,业务中做的时候需要了解。这种无序性,如果业务需要有顺序
最后用线性阻塞的编程,减少用并发。
推荐阅读
- let’s go——2022年读书活动招募书(第1期)
- go源码阅读 container/ring
- GO实践笔记
- go|vue-element-admin 后台动态加载菜单
- 迈向高级的Java面试突围课|完结
- 2022最新慕课网实战课程大全_资料完整_百度网盘分享
- 执行 go vendor 时第三方包 go 版本冲突问题的解决方法
- golang网络数据交换
- go reflect struct (go反射与struct结构体)