This article mainly introduces to you about the Golang in the For-loop and goroutine problems Related materials, the text through the sample code introduced in very detailed, for everyone to learn or use Golang has a certain reference learning value, the need for friends below along with small to learn together.
Background
Recently, in the course of studying the MIT distributed course 6.824, there were some problems with go implementation of the raft protocol. Share it for everyone to refer to the study, the following words do not say more, come together to see the detailed introduction bar.
See the following code:
For I: = 0; I < Len (rf.peers); i++ { dprintf ("i =%d", i) if i = = rf.me { dprintf ("Skipping Myself #%d", rf.me) continue } go fun C () { dprintf ("len of rf.peers =%d", Len (rf.peers)) dprintf ("Server #%d sending request vote to server%d", RF.M E, i) reply: = &requestvotereply{} OK: = Rf.sendrequestvote (i, args, reply) if ok && reply. votegranted && reply. term = = rf.currentterm { rf.votecount++ if Rf.votecount > Len (rf.peers)/2 { rf.winelectionch <- True }}} ()}
Where the length of the peers slice is 3, so the highest subscript is 2, in the non-parallel programming code in the For-loop should be very intuitive, I was not aware of the problem. However, during the debugging process, the index out of bounds error has been reported. The debug message shows that I has a value of 3, and I was wondering if the loop condition is obviously I < 2, how it becomes 3.
Analysis
Although I do not understand what happened, I know it should be caused by the goroutine introduced in the loop. After Google, found that go Wiki has a page Common mistake-using goroutines on loop iterator variables specifically mentioned this problem, it seems really very Common ah, laugh cry ~
Beginners often use the following code to process data in parallel:
For Val: = range Values {go val. MyMethod ()}
Or use closures (closure):
For Val: = range Values {go func () { fmt. Println (Val)} ()}
The problem here is that Val is actually a single variable that traverses all the data in the slice. Since closures are only bound to this Val variable, it is very likely that the above code will run with the result that all goroutine output the last element of the slice. This is because it is likely that goroutine will not begin execution until For-loop executes, and this time Val's value points to the last element in the slice.
The Val variable in the above loops are actually a single variable this takes on the value of each slice element. Because The closures is all bound to that one variable, there was a very good chance that's when you run this code would see the last element printed for every iteration instead of each value in sequence, because the Goroutines would proba Bly not begin executing until after the loop.
Workaround
The code above is correctly worded as:
For Val: = range Values {Go func (val interface{}) { fmt. Println (Val)} (Val)}
Here Val is passed as a parameter to the Goroutine, and each Val is computed independently and saved to the Goroutine stack, resulting in the desired result.
Another way is to define a new variable within the loop, because the variables defined within the loop are not shared during the loop traversal, so the same effect can be achieved:
For I: = Range Valslice {val: = Valslice[i] Go func () { fmt. Println (Val)} ()}
The simplest solution to the problem mentioned at the beginning of the article is to add a temporary variable within the loop and replace the I in the goroutine with this temporary variable:
Server: = I
Summarize