As a language with excellent concurrency performance, Go reduces the difficulty of concurrent development of coroutines, but it also has some concurrency pitfalls, which requires extra attention in development.
Here are a few more Go concurrency related tips to help you avoid some of the pitfalls of Go concurrency development.
Problems with closures passing parameters
The first is the problem with the closure passing parameters when the loop is concurrent, as shown in the following error example:
func main(a) {
for i := 0 ; i < 5 ; i++{
go func(a) {
fmt.Println("current i is ", i)
}()
}
time.Sleep(time.Second)
}
Copy the code
The most likely output of this code is:
current i is 5
current i is 5
current i is 5
current i is 5
current i is 5
Copy the code
This is because the address space used by I is reused in the loop, and while the Goroutine is executing, the value of I may be modified by the main Goroutine while other Goroutines are reading and using it, resulting in a concurrency error. This error can be avoided by copying or passing reference copies, as shown below:
func main() {
for i := 0 ; i < 5 ; i++{
go func(v int) {
fmt.Println("current i is", v)
}(i)
}
time.Sleep(time.Second)
}
Copy the code
Abnormal panic
In our previous article on Panic, we learned that the occurrence of the Panic exception caused the Go program to crash. However, even if Panic appears in other goroutine, it will cause the Go program to crash and exit. Meanwhile, Panic can only catch the exceptions of Goroutine itself. Therefore, for every goroutine started, panic should be caught at the entrance. And try to print stack information and exception handling, so as to avoid the panic of the child Goroutine causing the entire program to crash and exit. See the following example:
func RecoverPanic(a) {
// Recover from Panic and print the stack information
if e := recover(a); e ! =nil {
buf := make([]byte.1024)
buf = buf[:runtime.Stack(buf, false)]
fmt.Printf("[PANIC]%v\n%s\n", e, buf)
}
}
func main(a) {
for i:= 0 ; i < 5 ; i++{
go func(a) {
// defer registered the panic capture function
defer RecoverPanic()
dothing()
}()
}
}
Copy the code
Timeout control
The final tip is to be good at using a combination of SELECT, timer, and context for timeout control. If a goroutine is used for a long operation, it is better to add a timeout timer and pass the context in the concurrent operation, so that there will be no missing when canceling the goroutine, so as to achieve the purpose of retrieving goroutine and avoid memory leaks. As shown in the following example, using SELECT to listen for both task and timer status, when the timer arrives and the task is not completed, the task is terminated early, the resource is cleaned up, and the task is returned.
select {
// do logic process
case msg <- input:
....
// has been canceled
case <-ctx.Done():
/ /... Resources to clean up
return
// 2 second timeout
case <-time.After(time.Second * 2)
/ /... Resources to clean up
return
default:}Copy the code
summary
This article focuses on some of the concurrency development techniques that are common in Go. The author also just put forward three skills of concurrent development skills, in fact, not only Go concurrency, we need to pay attention to sum up all aspects of development skills in daily development.
Serverless architecture without servers?
In microservice architecture, ELK is used for log collection and unified processing
How do I handle Go error exceptions without a try-catch?