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

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

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

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
(0)

相关推荐

  • 在Go中,你犯过这些错误吗

    Go语言中文网 今天 以下文章来源于吴亲强的深夜食堂 ,作者吴亲库里 吴亲强的深夜食堂关注一些奇奇怪怪的设计,分享一些有有趣趣的生活 迭代器变量上使用 goroutine 这算高频吧. package ...

  • Go基础及语法(一)

    Go语言文件基础语法 package main //管理定义模块import "fmt" //导入模块/*主函数打印Hello,world!*/func main() {fmt.P ...

  • [基础语法]-第002节:常量的使用

    一.常量的使用 1.1 常量声明 常量是一个简单值的标识符,在程序运行时,不会被修改的量. const identifier [type] = value 显式类型定义: const b string ...

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

    原文链接:http://www.zhoubotong.site/post/24.html 直入正题,我们看下以下代码: package mainimport ( "encoding/json ...

  • RTOS中的任务是线程、进程、还是协程?

    今天为大家讲解讲解OS中的线程.进程和协程的这几个概念,同时一起看看RTOS中的任务到底属于哪一种. 1.三者整体关系图 很多小伙伴在学习OS的过程中会遇到各种程序形态,比如说进程.线程.协程.管程. ...

  • 【原创教程】houdini16.5全局变量VS局部变量-总结3种自定义局部变量的方法

    --  微资讯 · 微课程  -- 利用零碎时间,走上超神之路! 教程介绍 教程名字   CGhunter_houdini_02_002_05_Hscriptoverview 教程导读 整体介绍了ho ...

  • 【微总结】二张图总结houdini H上传cript中的全局变量和局部变量

    --  微资讯 · 微课程  -- 利用零碎时间,走上超神之路! 虽然houdini新版本中不在建议大量使用Hscript中的局部变量,但是由于某些全局变量和局部变量非常方便并且不具有替代性,特别是有 ...

  • 协程库 libtask 源码分析

    本文在公司内网有不错的反响,但不同于传统的前端技术文章,所以阅读起来可能有点晦涩. 假设读者已经了解了协程的概念.实现协程的底层技术支持,基于底层基础,我们来看看如何实现协程以及协程的应用. 什么是 ...

  • JavaScript全局变量与局部变量

    WEB前端开发社区 今天 在学习JavaScript的变量作用域之前,我们应当明确几点: JavaScript的变量作用域是基于其特有的作用域链的. JavaScript没有块级作用域. 函数中声明的 ...

  • 深入分析 Java、Kotlin、Go 的线程和协程

    前言 协程是什么 协程的好处 进程 进程是什么 进程组成 进程特征 线程 线程是什么 线程组成 任务调度 进程与线程的区别 线程的实现模型 一对一模型 多对一模型 多对多模型 线程的"并发& ...

  • gevent-使用greenlet的基于协程的Python网络库

    什么是 gevent? gevent 是一个基于协程的Python网络库,它使用 greenlet在libev 或libuv事件循环之上提供高级同步 API . 功能包括: 基于libev或libuv ...

  • 破解 Kotlin 协程 - 入门篇

    本文转自 Bennyhuo 的博客, 原文地址:https://www.bennyhuo.com/2019/04/01/basic-coroutines/ 破解 Kotlin 协程 - 入门篇 1. ...

  • 破解 Kotlin 协程(2) - 协程启动篇

    本文转自 Bennyhuo 的博客 原文地址:https://www.bennyhuo.com/2019/04/08/coroutines-start-mode/ 破解 Kotlin 协程 (2) - ...