我在做一个关于Golang的在线课程.课程material 中提供了以下代码,作为误用sync.Once的示例:

var (
    once sync.Once
    db   *sql.DB
)

func DbOnce() (*sql.DB, error) {
    var err error
    once.Do(func() {
        fmt.Println("Am called")
        db, err = sql.Open("mysql", "root:test@tcp(127.0.0.1:3306)/test")
        if err != nil {
            return
        }
        err = db.Ping()
    })
    if err != nil {
        return nil, err
    }
    return db, nil
}

据推测,以上是SQL连接管理器的错误实现.我们,学生们,要自己go 发现错误,这是我努力克服的.即使并行运行,代码也运行良好.我就是这样使用它的:

func main() {
    wg := sync.WaitGroup{}
    wg.Add(10)

    for i := 0; i < 10; i++ {
        go (func() {
            db, err := DbOnce()
            if err != nil {
                panic(err)
            }

            var v int
            r := db.QueryRow("SELECT 1")
            err = r.Scan(&v)
            fmt.Println(v, err)

            wg.Done()
        })()
    }

    wg.Wait()
}

我知道这里不鼓励学生提出家庭作业(job)问题,所以我不是在要求一个完整的解决方案,只是一个提示就可以了.错误是否与并发性有关(即,我需要在特定的并发上下文中运行它)?这就是sql的用法.特别开放?

推荐答案

db变量的初始化正常.问题在于返回的错误.

如果您第一次调用DbOnce(),但打开数据库连接失败,则会正确返回该错误.但是接下来的电话呢?db初始化代码将不再运行,因此可以返回nil db,并且由于初始化代码未运行,err变量的默认值将返回nil.总之,初始化错误已丢失,将不再报告.

一种解决方案是,如果连接失败(第一次通话时),停止应用程序.另一个选项是将初始化错误与db一起存储在包级别变量中,并从DbOnce()返回(不使用局部变量).前者的优点是,你不必处理DbOnce()返回的错误,因为它甚至不必返回错误(如果有错误,你的应用程序将终止).

后者可能是这样的:

var (
    once  sync.Once
    db    *sql.DB
    dbErr error
)

func DbOnce() (*sql.DB, error) {
    once.Do(func() {
        fmt.Println("Am called")
        db, dbErr = sql.Open("mysql", "root:test@tcp(127.0.0.1:3306)/test")
        if dbErr != nil {
            return
        }
        dbErr = db.Ping()
    })
    return db, dbErr
}

Go相关问答推荐

Term~T中的类型不能是类型参数,但可以是引用类型参数的切片

Golang文本/模板序列范围

Golang XML密钥名称冲突

如何给杜松子wine 的路由加上一个名字,比如Laravel ?

从文件读取字节,将其保存到 struct 体并修改值

当我使用 CircleCI 构建 Go Image 时,我得到runtime/cgo: pthread_create failed: Operation not allowed

使用 os/exec 和在命令行执行之间的结果莫名其妙地不同

使用图像解码 JPEG 时 colored颜色 不正确.解码并写入 PDF?

如何使用带有方法的字符串枚举作为通用参数?

Golang - 将 [8] 布尔转换为字节

Go 并发、goroutine 同步和关闭通道

切片到数组指针的转换

如果服务器在客户端的 gRPC 中不可用,则等待的方法

当 git clone 工作时,Go mod tidy 在私有存储库上失败

golang 如何从字符串中查找表情符号?

如何在 Unmarshal 中使用泛型(转到 1.18)

手动将 OpenTelemetry 上下文从 golang 提取到字符串中?

行之间的模板交替设计

go routine 和接收错误或成功的通道

为什么 Go 不允许将一个泛型分配给另一个泛型?