Ilya Sevostyanov | c289dec | 2016-11-10 14:04:40 +0300 | [diff] [blame] | 1 | Table of Contents |
| 2 | ================= |
| 3 | |
| 4 | + [Introduction](#introduction) |
| 5 | + [Using goroutines on loop iterator variables](#using-goroutines-on-loop-iterator-variables) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 6 | |
| 7 | # Introduction |
| 8 | |
| 9 | When 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_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 11 | # Using goroutines on loop iterator variables |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 12 | |
Eno Compton | 6cf536b | 2016-11-13 11:26:27 -0700 | [diff] [blame] | 13 | When iterating in Go, one might also be tempted to use goroutines to process data in parallel. For example, you might write the following code: |
Matthias Lüdtke | 8c2cac4 | 2015-07-07 18:51:23 +0200 | [diff] [blame] | 14 | ```go |
Cristian Măgherușan-Stanciu @magheru_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 15 | for val := range values { |
Eno Compton | 6cf536b | 2016-11-13 11:26:27 -0700 | [diff] [blame] | 16 | go val.MyMethod() |
Cristian Măgherușan-Stanciu @magheru_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 17 | } |
| 18 | ``` |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 19 | |
Cristian Măgherușan-Stanciu @magheru_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 20 | or if you wanted to process values coming in from a channel in their own goroutines, you might write something like this, using a closure: |
| 21 | |
Matthias Lüdtke | 8c2cac4 | 2015-07-07 18:51:23 +0200 | [diff] [blame] | 22 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 23 | for val := range values { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 24 | go func() { |
| 25 | fmt.Println(val) |
| 26 | }() |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 27 | } |
| 28 | ``` |
| 29 | |
Cristian Măgherușan-Stanciu @magheru_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 30 | |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 31 | If you don't immediately see the problem with the above code, take a second to see if you can figure out what is wrong with it before you keep reading. |
| 32 | |
Cristian Măgherușan-Stanciu @magheru_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 33 | The ` val ` variable in the above loops 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 Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 34 | |
Cristian Măgherușan-Stanciu @magheru_san | 05efe8e | 2015-04-12 15:34:34 +0200 | [diff] [blame] | 35 | The proper way to write that closure loop is: |
Matthias Lüdtke | 8c2cac4 | 2015-07-07 18:51:23 +0200 | [diff] [blame] | 36 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 37 | for val := range values { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 38 | go func(val interface{}) { |
| 39 | fmt.Println(val) |
| 40 | }(val) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 41 | } |
| 42 | ``` |
| 43 | |
| 44 | By 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. |
| 45 | |
| 46 | It 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: |
| 47 | |
Matthias Lüdtke | 8c2cac4 | 2015-07-07 18:51:23 +0200 | [diff] [blame] | 48 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 49 | for i := range valslice { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 50 | val := valslice[i] |
| 51 | go func() { |
| 52 | fmt.Println(val) |
| 53 | }() |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 54 | } |
| 55 | ``` |
| 56 | |
| 57 | Note that without executing this closure as a goroutine, the code runs as expected. The following example prints out the integers between 1 and 10. |
| 58 | |
Matthias Lüdtke | 8c2cac4 | 2015-07-07 18:51:23 +0200 | [diff] [blame] | 59 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 60 | for i := 1; i <= 10; i++ { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 61 | func() { |
| 62 | fmt.Println(i) |
| 63 | }() |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 64 | } |
| 65 | ``` |
| 66 | |
| 67 | Even 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. |
| 68 | |
oleksiif | 917f086 | 2015-12-05 12:04:43 +0700 | [diff] [blame] | 69 | http://golang.org/doc/go_faq.html#closures_and_goroutines |