关于go:go-for循环中的作用域

8次阅读

共计 1449 个字符,预计需要花费 4 分钟才能阅读完成。

一、介绍
var a int
a := 0

二、for 循环 go 协程作用域
1. 咱们先看下最简略的 for 如下

package main

import "fmt"

func main() {
   for i := 0; i < 2; i++ {go func() {fmt.Println(i)
      }()}
}

下面的代码,最初没有输入。咱们的冀望输入 0 1 的欲望落空了,什么起因?
程序执行工夫图

如上图,咱们发现在下面的程序有 2 种状况
1. 当主过程完结,goroutine2,goroutine3 都还没执行完,则没有输入。
2. 状况 2,如下图 goroutine2 执行完,输入了 1 !!!! 留神不是 0!!!!

从下面学到的解决竞态 4 中计划,咱们用 waitGroup 阻塞主过程,改良代码如下:

package main

import (
   "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 main

import (
   "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 main

import (
   "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 中的执行程序是 无序的,业务中做的时候须要理解。这种无序性,如果业务须要有程序
最初用线性阻塞的编程,缩小用并发。

正文完
 0