blob: 3ae5c174e183fa86fb07520646f457372e44e751 [file] [log] [blame] [view]
Ilya Sevostyanovc289dec2016-11-10 14:04:40 +03001Table of Contents
2=================
3
4+ [Introduction](#introduction)
xiao东篱f07727c2018-05-31 17:53:45 +08005+ [Using goroutines on loop iterator variables](#using-goroutines-on-loop-iterator-variables)
Andrew Gerrand5bc444d2014-12-10 11:35:11 +11006
7# Introduction
8
9When new programmers start using Go or when old Go programmers start using a new concept, there are some common mistakes that many of them make. Here is a non-exhaustive list of some frequent mistakes that show up on the mailing lists and in IRC.
10
Cristian Măgherușan-Stanciu @magheru_san05efe8e2015-04-12 15:34:34 +020011# Using goroutines on loop iterator variables
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110012
Rich Parrishb1ebeff2018-06-14 10:55:55 -070013When iterating in Go, one might attempt to use goroutines to process data in parallel. For example, you might write something like this, using a closure:
Cristian Măgherușan-Stanciu @magheru_san05efe8e2015-04-12 15:34:34 +020014
Matthias Lüdtke8c2cac42015-07-07 18:51:23 +020015```go
Mingliang Liu88d63a92018-04-19 20:53:41 -070016for _, val := range values {
Dave Day0d6986a2014-12-10 15:02:18 +110017 go func() {
18 fmt.Println(val)
19 }()
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110020}
21```
22
Pete Vilter1b5ef172018-04-01 19:49:29 -040023The above for loops might not do what you expect because their ` val ` variable is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110024
Cristian Măgherușan-Stanciu @magheru_san05efe8e2015-04-12 15:34:34 +020025The proper way to write that closure loop is:
Matthias Lüdtke8c2cac42015-07-07 18:51:23 +020026```go
Mingliang Liu88d63a92018-04-19 20:53:41 -070027for _, val := range values {
Dave Day0d6986a2014-12-10 15:02:18 +110028 go func(val interface{}) {
29 fmt.Println(val)
30 }(val)
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110031}
32```
33
34By adding val as a parameter to the closure, ` val ` is evaluated at each iteration and placed on the stack for the goroutine, so each slice element is available to the goroutine when it is eventually executed.
35
36It is also important to note that variables declared within the body of a loop are not shared between iterations, and thus can be used separately in a closure. The following code uses a common index variable ` i ` to create separate ` val `s, which results in the expected behavior:
37
Matthias Lüdtke8c2cac42015-07-07 18:51:23 +020038```go
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110039for i := range valslice {
Dave Day0d6986a2014-12-10 15:02:18 +110040 val := valslice[i]
41 go func() {
42 fmt.Println(val)
43 }()
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110044}
45```
46
47Note that without executing this closure as a goroutine, the code runs as expected. The following example prints out the integers between 1 and 10.
48
Matthias Lüdtke8c2cac42015-07-07 18:51:23 +020049```go
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110050for i := 1; i <= 10; i++ {
Dave Day0d6986a2014-12-10 15:02:18 +110051 func() {
52 fmt.Println(i)
53 }()
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110054}
55```
56
57Even though the closures all still close over the same variable (in this case, ` i `), they are executed before the variable changes, resulting in the desired behavior.
xiao东篱ebb612f2018-05-31 19:45:58 +080058http://golang.org/doc/go_faq.html#closures_and_goroutines
Andrew Gerrand5bc444d2014-12-10 11:35:11 +110059
xiao东篱ebb612f2018-05-31 19:45:58 +080060Another similar situation that you may find like following:
61
62```go
63for _, val := range values {
64 go val.MyMethod()
65}
66
67func (v *val) MyMethod() {
68 fmt.Println(v)
69}
70```
DhanaRaj Durairajf130e762018-06-13 19:52:38 +053071The above example also will print last element of values, the reason is same as closure. To fix the issue declare another variable inside the loop.
xiao东篱ebb612f2018-05-31 19:45:58 +080072
73```go
74for _, val := range values {
75 newVal := val
76 go newVal.MyMethod()
77}
78
79func (v *val) MyMethod() {
80 fmt.Println(v)
81}
82```