This is a creation in Article, where the information may have evolved or changed.
The external variables of the Go language's closure capture, I'm still accustomed to Lua, called Upvalue, after all, go draws on a lot of LUA features.
Let's take a look at five almost the same code snippet first.
package mainimport ("log""sync")func main() {wg := sync.WaitGroup{}for i := 0; i < 5; i++ {wg.Add(1)go func(wg sync.WaitGroup, i int) {log.Printf("i:%d", i)wg.Done()}(wg, i)}wg.Wait()log.Println("exit")}
Output:
go run wgtest1.go 2017/01/01 23:43:08 i:42017/01/01 23:43:08 i:22017/01/01 23:43:08 i:32017/01/01 23:43:08 i:12017/01/01 23:43:08 i:0fatal error: all goroutines are asleep - deadlock!goroutine 1 [semacquire]:sync.runtime_Semacquire(0xc42000a2ac)/usr/local/Cellar/go/1.7.4_1/libexec/src/runtime/sema.go:47 +0x30sync.(*WaitGroup).Wait(0xc42000a2a0)/usr/local/Cellar/go/1.7.4_1/libexec/src/sync/waitgroup.go:131 +0x97main.main()/Users/linkerlin/gos/wgtest1.go:17 +0xbaexit status 2
This is because the waitgroup in the Go language is an object that cannot be copied after the first use. The main function of Goroutine is actually the method of transmitting the value of Waitgroup. It is particularly important to note that the output of the next i is in line with expectations.
OK, let's look at the second piece of code:
package mainimport ("log""sync")func main() {wg := sync.WaitGroup{}for i := 0; i < 5; i++ {wg.Add(1)go func() {log.Printf("i:%d", i)wg.Done()}()}wg.Wait()log.Println("exit")}
Output:
go run wgtest2.go 2017/01/01 23:48:10 i:52017/01/01 23:48:10 i:52017/01/01 23:48:10 i:52017/01/01 23:48:10 i:52017/01/01 23:48:10 i:52017/01/01 23:48:10 exit
There is no deadlock, but the output of the I value is wrong. Because, the go language inside Upvalue is quoted. Goroutine multiple captures are the same i.
Again, let's look at the third piece of code:
package mainimport ("log""sync")func main() {wg := sync.WaitGroup{}for i := 0; i < 5; i++ {wg.Add(1)go func() {log.Printf("i:%d", i)wg.Done()}()}wg.Wait()log.Println("exit")}
Output:
go run wgtest3.go 2017/01/01 23:51:46 i:52017/01/01 23:51:46 i:52017/01/01 23:51:46 i:52017/01/01 23:51:46 i:42017/01/01 23:51:46 i:52017/01/01 23:51:46 exit
There is no deadlock, I is the wrong number. Because the I of Upvaule is ByRef pass. Note that here there are 4 5 and one 4, the final output what is actually random, depending on the operating system and hardware. The faster the goroutine is dispatched, the more likely it is to have a smaller output than 5.
Again, let's look at the fourth piece of code:
package mainimport ("log""sync")func main() {wg := sync.WaitGroup{}for i := 0; i < 5; i++ {wg.Add(1)go func(wg *sync.WaitGroup, i int) {log.Printf("i:%d", i)wg.Done()}(&wg, i)}wg.Wait()log.Println("exit")}
Output:
go run wgtest4.go 2017/01/01 23:56:51 i:12017/01/01 23:56:51 i:02017/01/01 23:56:51 i:42017/01/01 23:56:51 i:22017/01/01 23:56:51 i:32017/01/01 23:56:51 exit
Everything is normal and in line with expectations. However, this kind of writing is rather cumbersome. First, instead of using closures to build a higher-order function, it reverts to the traditional upvalue, which is too heavy for the mind of the person who wrote the code, and the values and references to be specified manually, and also in the Goroutine main function entry one by one. So what should we recommend as a way of writing?
Finally, let's look at the fifth piece of code:
package mainimport ("log""sync")func main() {wg := sync.WaitGroup{}for i := 0; i < 5; i++ {func(i int) {wg.Add(1)go func() {log.Printf("i:%d", i)wg.Done()}()}(i)}wg.Wait()log.Println("exit")}
Output:
go run wgtest5.go 2017/01/02 00:03:32 i:42017/01/02 00:03:32 i:02017/01/02 00:03:32 i:12017/01/02 00:03:32 i:22017/01/02 00:03:32 i:32017/01/02 00:03:32 exit
The same things are normal. However, in the fifth code, the main function of Goroutine is not a parameter. The quoted situation takes advantage of the upvalue, and the I variable that needs to be passed is copied with the parameters of an outsourced function. Because each loop invokes the outsource function, it duplicates the value of I, although the goroutine main function in the inner layer captures I through upvalue, but each captures the I copy of the outsourced function.
In summary, in the consideration of reducing the mental burden of developers, I suggest:
1. The Goroutine entry function inside the go language does not pass arguments.
2. All ref parameters are captured by Upvalue.
3. If you want to pass a value, you can wrap a function outside of goroutine and pass the value of the parameter to the outsourced function. The name of the parameter remains the same name.