当前位置:首页 > Golang > 正文内容

go协程全局变量和局部变量

周伯通2021-01-10Golang188

大家可能经常会用到类似如下代码片段:

package main

import (
   "fmt"
   "sync"
   "time"
)

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   wg := sync.WaitGroup{}
   for k, v := range sli {
      wg.Add(1)
      go func() {
         time.Sleep(time.Second)
         fmt.Println(k, v)
         wg.Done()
      }()
   }
   wg.Wait()
}

打印输出:

9 9
9 9
9 9
9 9
9 9
9 9
9 9
9 9
9 9
9 9

结果是不是和想象的不一样?,主要原因出在协程这里,如果不使用协程,直接使用串行的方式,结果结合预期一致,比如:

package main

import (
   "fmt"
   "time"
)

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   for k, v := range sli {
      func() {
         time.Sleep(time.Second)
         fmt.Println(k, v)
      }()
   }
}

打印输出:

0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9

那为什么上面使用携程的输出都是相同值?我们来解读下:
其中 k, v 是迭代变量,每次迭代都会给 k, v 赋值新的值,并且多个协程又同时调用了 k, v ,所以结果就串了,那携程怎么解决?解决方式我们可以定义一个局部变量。

package main

import (
   "fmt"
   "sync"
   "time"
)

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   wg := sync.WaitGroup{}
   for k, v := range sli {
      wg.Add(1)
      k1 := k
      v1 := v
      go func() {
         time.Sleep(time.Second)
         fmt.Println(k1, v1)
         wg.Done()
      }()
   }
   wg.Wait()
}

k1, v1 是局部变量,每次循环,循环体内是不共享的,这也是为什么可以这样声明变量(k1 := k)。

或者通过传参的方式来固定下来,比如像下面这样:

package main

import (
   "fmt"
   "sync"
   "time"
)

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   wg := sync.WaitGroup{}
   for k, v := range sli {
      wg.Add(1)

      go func(k, v interface{}) {
         time.Sleep(time.Second)
         fmt.Println(k, v)
         wg.Done()
      }(k, v)
   }
   wg.Wait()
}

这样输出就正常,比如输出如下:

0 0
5 5
2 2
3 3
4 4
1 1
9 9
6 6
8 8
7 7


    扫描二维码推送至手机访问。

    版权声明:本文由周伯通的博客发布,如需转载请注明出处。

    本文链接:http://zhoubotong.site/post/19.html

    分享给朋友:

    相关文章

    Go 自定义error错误

        Go的error比较灵活.但是自身对error处理的机制有不太好用,我们可以自定义错误输出: 只要所有实现了 Error()&nbs...

    关于go协程同步的几种方法

    这里咋们简要介绍下关于go中协程的几种同步方法。先说下概念性的:协程概念简要理解协程有点类似线程,是一种更为轻量级的调度单位,但协程还是不同于线程的,线程是系统级实现的,常见的调度方法是时间片轮转法,...

    Go 数组合并去重和排序

            Sort包实现了四种基本排序算法:插入排序、归并排序、堆排序和快速排序。 但是这四种排序方法是不公开的,...

    Go map定义的几种方式以及修改技巧

    直入正题,我们看下以下代码:package main import (    "encoding/json"  ...

    Go channel 协程为什么是安全的

        Channel跟java/php的 thread不一样,首先channel是协程不是线程。channel不会产生新的线程,自然不会涉及到新的进程或者线程...

    发表评论

    访客

    看不清,换一张

    ◎欢迎参与讨论,请在这里发表您的看法和观点。