golang资源所有权模式(文件、连接、关闭)

golang资源所有权模式(文件、连接、关闭),go,resources,resource-management,Go,Resources,Resource Management,戈朗管理资源所有权的正确方式是什么?假设我有以下几点: db, err := sql.Open("mysql", "role@/test_db") am := NewResourceManager(db) am.DoWork() db.Close() 总是让调用函数维护关闭资源的所有权和责任是典型的吗?这让我觉得有点奇怪,因为在结束后,am仍然保留一个引用,如果我或其他人以后不小心,可以尝试使用db(我想这是延迟的情况;但是,如果我想将ResourceManageram从这个块传递回来,我怎么

戈朗管理资源所有权的正确方式是什么?假设我有以下几点:

db, err := sql.Open("mysql", "role@/test_db")
am := NewResourceManager(db)
am.DoWork()
db.Close()
总是让调用函数维护关闭资源的所有权和责任是典型的吗?这让我觉得有点奇怪,因为在结束后,
am
仍然保留一个引用,如果我或其他人以后不小心,可以尝试使用
db
(我想这是延迟的情况;但是,如果我想将ResourceManager
am
从这个块传递回来,我怎么能正确地延迟关闭文件?我实际上希望在这个块完成执行时它保持打开状态).我发现在其他语言中,我经常希望允许实例管理资源,然后在调用其析构函数时将其清除,如下面的玩具python示例:

class Writer():
   def __init__(self, filename):
       self.f = open(filename, 'w+')

   def __del__(self):
       self.f.close()

   def write(value):
       self.f.write(value)
不幸的是,golang中没有析构函数。我不确定在go中如何执行此操作,除了类似的操作:

type ResourceManager interface {
   DoWork()
   // Close() ?
}

type resourceManager struct {
  db *sql.DB
}

func NewResourceManager(db *sql.DB) ResourceManager {
  return &resourceManager{db}
} 

db, err := sql.Open("mysql", "role@/test_db")
am := NewResourceManager(db)
am.DoWork()
am.Close()  // using method shortening
但这似乎不那么透明,我不知道如何传达ResourceManager也需要靠近()现在,我发现这是一个常见的绊脚石,也就是说,我还希望有一个资源管理器来保持gRPC客户端连接,如果这些类型的资源不是由资源管理对象管理的,那么我的主要功能似乎会被大量的资源管理(即打开和关闭)弄得乱七八糟。例如,我可以想象一下,如果我不想
main
了解有关对象及其资源的任何信息:

...
func NewResourceManager() ResourceManager {
  db, err := sql.Open("mysql", "role@/test_db")
  return &resourceManager{db}
}
...
// main elsewhere
am := NewResourceManager()
am.DoWork()

您选择了一个糟糕的示例,因为您通常会重复使用数据库连接,而不是每次使用都打开和关闭数据库连接。因此,您会将db连接传递给使用它的函数,并在调用者中执行资源管理,而无需任何资源管理器:

// Imports etc omitted for the sake of readability

func PingHandler(db *sql.DB) http.Handler (
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
       if err := db.ping(); err != nil {
          http.Error(w,e.Error(),500)
       }
    })
)

func main(){
    db,_ := sql.Open("superdb",os.Getenv("APP_DBURL"))

    // Note the db connection will only be closed if main exits.
    defer db.Close()

    // Setup the server
    http.Handle("/ping", PingHandler(db))
    server := &http.Server{Addr: ":8080"}

    // Create a channel for listening on SIGINT, -TERM and -QUIT
    stop := make(chan os.Signal, 1)

    // Register channel to be notified on said signals
    signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

    go func(){
            // When we get the signal...
            <- stop
            ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
            // ... we gracefully shut down the server.
            // That ensures that no new connections, which potentially
            // would use our db connection, are accepted.
            if err := server.Shutdown(ctx); err != nil {
                // handle err
            }
    }

    // This blocks until the server is shut down.
    // AFTER it is shut down, main exits, the deferred calls are executed.
    // In this case, the database connection is closed.
    // And it is closed only after the last handler call which uses the connection is finished.
    // Mission accomplished.
    server.ListenAndServe()
}
//为了可读性,省略了导入等
func PingHandler(db*sql.db)http.Handler(
返回http.HandlerFunc(func(w http.ResponseWriter,r*http.Request){
如果错误:=db.ping();错误!=nil{
http.Error(w,e.Error(),500)
}
})
)
func main(){
db,u:=sql.Open(“superdb”,os.Getenv(“APP_DBURL”))
//注:只有在主出口处,db连接才会关闭。
延迟db.Close()
//安装服务器
句柄(“/ping”,PingHandler(db))
服务器:=&http.server{Addr::8080}
//创建一个用于监听SIGINT、-TERM和-QUIT的频道
停止:=接通(切换操作信号,1)
//注册要在所述信号上通知的信道
signal.Notify(stop、syscall.SIGINT、syscall.SIGTERM、syscall.SIGQUIT)
go func(){
//当我们收到信号时。。。

这个问题很广泛,唯一正确的答案是“视情况而定”。