内部函数可以安全地引用外部函数的值吗?

huangapple go评论75阅读模式
英文:

Safe to refer to values in outer function from inner function?

问题

我正在从Node.js转向Go,并且我担心在Go中是否可以安全地使用我在Node中使用的构造,并且是否有更符合惯用方式的方法来完成相同的事情。我正在使用Echo框架,并希望设置一个特定路由的结构体,该结构体将在上下文对象中可用。我可以在每次调用中的中间件中生成结构体,但这样做的成本很高。相反,我在外部函数中设置一次结构体,然后返回一个引用外部函数中结构体的内部函数。我希望这样一来,我只需要一次生成成本,然后在每次调用时都有与我的路由关联的正确结构体。

这段代码有什么问题吗?它会导致垃圾回收方面的问题吗?有没有更高效的方法来完成相同的事情?我假设每次调用中间件时都会复制结构体的副本。

英文:

I am moving to Go from Node.js and I am concerned whether a construct that I would use in Node is safe to do in Go and whether there is a more idiomatic way to accomplish the same thing. I am using the Echo framework and want to set a route specific struct that will be available within the context object. I could generate the struct for every call within middleware, but it's expensive to do so. Instead, I set the struct once in an outer func that then returns an inner func that refers to the struct in the outer func. My hope is that I then only incur the generation cost once and then have the correct struct associated with my route for every call.

e.POST(path, POST.GenericPostHandler, func(next echo.HandlerFunc)  echo.HandlerFunc {
	operation := getOperationMap(path)
	return func(c echo.Context) error {
		c.Set("op", operation)
		return next(c)
	}
})

Are there any concerns with this code? Will it cause problems with GC? Is there a more efficient way to accomplish the same thing? I assume a copy of the struct is made every time the middleware is called.

答案1

得分: 1

这段代码是安全的,不会引起垃圾回收问题,并且是一种在Go语言中可以使用的良好、惯用的模式。

在你的示例中,只会创建一个operation,将其移动到堆上,然后在每个请求处理时共享。

当我需要在处理所有请求时初始化一个昂贵的结构体时,我经常使用这个确切的模式。

英文:

This code is safe, won't cause GC problems, and is a good, idiomatic pattern that can be used in Go.

In your example, only one operation will be created, moved to the heap, and then shared by each request as they are handled by Echo.

I often use this exact pattern myself when I need to initialize an expensive struct that will be used when handling all requests.

答案2

得分: 0

如果operationMap在初始化后不会改变,你可以将其声明为单例实例,如下所示:

package main

import (
	"fmt"
	"sync"
)

var (
	operationMapInst map[string]string // 我不知道确切的 map 类型,所以你需要更改类型。
	operationMapOnce sync.Once
)

func getOperationMap() map[string]string {
	// operationMapOnce.Do() 只会在第一次调用 getOperationMap() 时运行一次。
	operationMapOnce.Do(func() {
		// 初始化 operationMapInst。
		operationMapInst = map[string]string{"/": "root", "/ver": "version"}
		fmt.Println("operaionMap 已初始化!")
	})

	return operationMapInst
}

func main() {
	// 初始化逻辑只会运行一次。
	// 因为 getOperationMap() 返回的是 map,
	// 路径的值的语法应该是 getOperationMap()[path],
	// 而不是 getOperationMap(path)。
	rootOp, ok := getOperationMap()["/"]
	fmt.Println(rootOp, ok)

	// 重复
	rootOp, ok = getOperationMap()["/"]
	fmt.Println(rootOp, ok)
	verOp, ok := getOperationMap()["/ver"]
	fmt.Println(verOp, ok)
	verOp, ok = getOperationMap()["/ver"]
	fmt.Println(verOp, ok)
}

你可以在这里运行这段代码。

我推荐阅读 http://marcio.io/2015/07/singleton-pattern-in-go/ 了解 Go 中的单例模式。

英文:

If operationMap never changes after initialization, You can declare operationMap as a singleton instance like following:

package main

import (
	"fmt"
	"sync"
)

var (
	operationMapInst map[string]string // I don't know the exact type of map, so you should change the type.
	operationMapOnce sync.Once
)

func getOperationMap() map[string]string {
	// operationMapOnce.Do() runs only once
	// when the first time getOperationMap() is called.
	operationMapOnce.Do(func() {
		// Initialize operationMapInst.
		operationMapInst = map[string]string{"/": "root", "/ver": "version"}
		fmt.Println("operaionMap has initialized!")
	})

	return operationMapInst
}

func main() {
	// The initialization logic runs only once.
	// Because getOperationMap() returns map,
	// syntax for the value for a path should be getOperationMap()[path],
	// not getOperationMap(path).
	rootOp, ok := getOperationMap()["/"]
	fmt.Println(rootOp, ok)
	
	// repetition
	rootOp, ok = getOperationMap()["/"]
	fmt.Println(rootOp, ok)
	verOp, ok := getOperationMap()["/ver"]
	fmt.Println(verOp, ok)
	verOp, ok = getOperationMap()["/ver"]
	fmt.Println(verOp, ok)
}

You can run this code here.

I recommend http://marcio.io/2015/07/singleton-pattern-in-go/ for understanding singleton pattern in Go.

huangapple
  • 本文由 发表于 2016年12月2日 13:50:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/40925639.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定