Goroutine 在 Windows 和 Linux 上的行为不同

Goroutine behaves differently on Windows and Linux

我是 GO 的新手。我有以下遗留代码。

var db *sql.DB

func init() {
    go feedChan()

    connString := os.Getenv("DB_CONN")
    var err error

    db, err = sql.Open("postgres", connString)
    if err != nil {
        log.Fatalf("Failed to connect to database at %q: %q\n", connString, err)
    }

    // confirm connection
    if err = db.Ping(); err != nil {
        log.Fatalf("Unable to ping database at %q: %q\n", connString, err)
    }
}


func feedChan() {
    selectQuery, err := db.Prepare(`
        SELECT id, proxy
        FROM proxy
        WHERE fail_count < 2
        ORDER BY date_added DESC, last_used ASC, fail_count ASC
        LIMIT 5
    `)
    ....

以下代码适用于 linux。但它在 windows 上失败,在

上出现 nil 错误
selectQuery, err := db.Prepare(`

这对我来说很有意义,因为 db 在 feedChan goroutine 启动后初始化。对我来说没有意义的是为什么它适用于 linux。

所以问题是为什么这段代码在 linux 时没有错误?

那可能是 race condition。导入 "time",将此行放在 go feedChan() 之后,看看它是否仍然适用于 Linux:

time.Sleep(3 * time.Second)

为了避免这种情况,您可以在 生成例程(使用 db)之前初始化 db 或使用某种barrier:

func init() {
    barrier := make(chan int)
    go feedChan(barrier)

    connString := os.Getenv("DB_CONN")
    var err error
    db, err = sql.Open("postgres", connString)

    if err != nil {
        log.Fatalf("Failed to connect to database at %q: %q\n", connString, err)
        // Retry.
    } else {
        barrier <- 1 // Opens barrier.
    }
    // ...
}


func feedChan(barrier chan int) {
    <-barrier // Blocks until db is ready.
    selectQuery, err := db.Prepare(`
        SELECT id, proxy
        FROM proxy
        WHERE fail_count < 2
        ORDER BY date_added DESC, last_used ASC, fail_count ASC
        LIMIT 5
    `)
    // ...
}

在阅读了你的函数的第一行之后,我只能说你的遗留代码有一个 巨大的 错误,只需移动这一行就可以轻松修复它 go feedChan()init() 函数的末尾。

另请注意,主要原因不是竞争条件,只是等待 db 变量正确初始化的问题。