- With issues:
- gin context 当函数返回后 goroutine中context的值可能受到影响
Description
1、访问http://127.0.0.1:8080/test/test?a=1 2、连续访问http://127.0.0.1:8080/test/test?a=2 3、正常输出: 1 ++++ 1 ++++ 1 ---------------------- 1 2 ++++ 2 ++++ 2 ---------------------- 2 2 ========== 2 =============== 1 ========== 1 =============== //正常 2 ++++ 2 ++++ 2 ---------------------- 2 2 ========== 2 =============== 异常输出: 1 ++++ 1 ++++ 1 ---------------------- 1 2 ++++ 2 ++++ 2 ---------------------- 2 2 ========== 2 =============== 2 ++++ 2 ++++ 2 ---------------------- 2 2 ========== 2 =============== 1 ========== 2 =============== //异常http://127.0.0.1:8080/test/test?a=1结果返回从上下文中取出a=2 2 ++++ 2 ++++ 2 ---------------------- 2 2 ========== 2 ===============
How to reproduce
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
func main() {
gin.SetMode("debug")
r := gin.New()
r.Use(func(ctx *gin.Context) {
ctx.Request = ctx.Request.WithContext(ctx)
ctx.Set("a", ctx.Query("a"))
value, _ := ctx.Get("a")
fmt.Println(ctx.Query("a"), "++++", value, "++++")
})
group := r.Group("/test")
group.GET("/test", func(ctx *gin.Context) {
go func(c *gin.Context) {
//c := &gin.Context{} //重新定以后恢复正常
//*c = *ctx
a := c.Query("a")
fmt.Println(a, "----------------------", c.Value("a"))
if a=="1"{
time.Sleep(3*time.Second)
}
fmt.Println(a, "==========", c.Value("a"), "===============") //此处输出可能异常
}(ctx)
//主函数不退出正常
//time.Sleep
})
url := "0.0.0.0:8080"
r.Run(url)
}
go 1.17.3 windows gin 1.8.1
**Comment From: liuliqiang**
I think the reason for this problem is that you still using the `gin.Context` after your handler return.
In gin's implementation, when the handler return, it will put the `gin.Context` into a pool for reuse, and it's most possible that next when you request `http://127.0.0.1:8080/test/test\?a\=2`, it will reuse the context and then assign the new value into the context. So after 3s later, your goroutine in the handler wakes up and get the value from the context will get the new value("2") instead of the old value("1").
So there will be two solutions for it:
1. Not use the original context in your running goroutine after the handler return
2. Like your comment codes, you can allocate a new context to store the data from the original context, and such the gin reuse original context will not affect your running goroutine.
**Comment From: liuliqiang**
And you can try to printout the pointer address for the context in your code, you will find out what happen, I just change the print function for your codes:
package main
import ( "fmt" "github.com/gin-gonic/gin" "strings" "time" )
func main() { gin.SetMode("debug") r := gin.New() r.Use(func(ctx gin.Context) { ctx.Request = ctx.Request.WithContext(ctx) ctx.Set("a", ctx.Query("a")) value, _ := ctx.Get("a") fmt.Printf("%s,%p,%s,%s\n", strings.Repeat("+", 10), &ctx, value, strings.Repeat("+", 5)) }) group := r.Group("/test") group.GET("/test", func(ctx gin.Context) {
go func(c *gin.Context) {
//c := &gin.Context{} //重新定以后恢复正常
//*c = *ctx
a := c.Query("a")
fmt.Println(a, "----------------------", c.Value("a"))
if a == "1" {
time.Sleep(3 * time.Second)
}
fmt.Printf("%s,%p,%s,%s\n", strings.Repeat("=", 10), &c, c.Value("a"), strings.Repeat("=", 10)) //此处输出可能异常
}(ctx)
//主函数不退出正常
//time.Sleep
})
url := "0.0.0.0:8080"
r.Run(url)
}
```
Comment From: dafeng2018
Please refer to the below link not sure if it will help you https://gin-gonic.com/docs/examples/goroutines-inside-a-middleware/