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?

Subscribe to the latest articles, welcome to follow my official account