go語言多Goroutine共享數(shù)據(jù)庫連接的優(yōu)雅關(guān)閉
在Go語言并發(fā)編程中,多個(gè)Goroutine共享數(shù)據(jù)庫連接是常見場景。不當(dāng)?shù)倪B接關(guān)閉方式可能導(dǎo)致數(shù)據(jù)丟失或程序崩潰。本文探討如何安全地關(guān)閉多個(gè)Goroutine共享的數(shù)據(jù)庫連接。
問題分析:
直接在主Goroutine使用defer db.Close()無法保證所有子Goroutine都已完成數(shù)據(jù)庫操作,可能導(dǎo)致連接提前關(guān)閉,引發(fā)錯(cuò)誤。在每個(gè)子Goroutine中使用defer db.Close()則會(huì)導(dǎo)致連接被多次關(guān)閉,同樣引發(fā)錯(cuò)誤。
解決方案:
立即學(xué)習(xí)“go語言免費(fèi)學(xué)習(xí)筆記(深入)”;
推薦使用計(jì)數(shù)器和sync.WaitGroup來協(xié)調(diào)Goroutine的執(zhí)行和數(shù)據(jù)庫連接的關(guān)閉。
示例代碼:
package main import ( "fmt" "sync" "time" "database/sql" _ "github.com/go-sql-driver/mysql" // 替換成你的數(shù)據(jù)庫驅(qū)動(dòng) ) type dbConn struct { conn *sql.DB wg *sync.WaitGroup } func openDb() (*dbConn, error) { db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database") // 替換成你的數(shù)據(jù)庫連接字符串 if err != nil { return nil, err } return &dbConn{conn: db, wg: &sync.WaitGroup{}}, nil } func (dc *dbConn) close() error { dc.wg.Wait() // 等待所有 Goroutine 完成 return dc.conn.Close() } func querydb(dc *dbConn, i int) { defer dc.wg.Done() dc.wg.Add(1) // 計(jì)數(shù)器加1 // ... 數(shù)據(jù)庫操作 ... time.Sleep(time.Second) // 模擬數(shù)據(jù)庫操作耗時(shí) fmt.Printf("Goroutine %d finishedn", i) } func main() { dc, err := openDb() if err != nil { fmt.Println("Error opening database:", err) return } defer dc.close() // 確保最后關(guān)閉連接 for i := 0; i < 5; i++ { go querydb(dc, i) } fmt.Println("Main goroutine waiting...") //time.Sleep(3*time.Second) // 可選:等待Goroutine完成,如果數(shù)據(jù)庫操作時(shí)間不確定,則需要其他機(jī)制等待 }
代碼解釋:
- dbConn 結(jié)構(gòu)體包含數(shù)據(jù)庫連接和sync.WaitGroup,用于管理Goroutine的執(zhí)行。
- openDb 函數(shù)打開數(shù)據(jù)庫連接并返回 dbConn 實(shí)例。
- close 方法使用 dc.wg.Wait() 等待所有 Goroutine 完成后,再關(guān)閉數(shù)據(jù)庫連接。
- querydb 函數(shù)在執(zhí)行數(shù)據(jù)庫操作前使用 dc.wg.Add(1) 增加計(jì)數(shù)器,操作完成后使用 defer dc.wg.Done() 減少計(jì)數(shù)器。
- main 函數(shù)啟動(dòng)多個(gè) Goroutine 執(zhí)行 querydb 函數(shù),最后使用 defer dc.close() 關(guān)閉數(shù)據(jù)庫連接。
這種方法確保所有 Goroutine 完成數(shù)據(jù)庫操作后,再關(guān)閉數(shù)據(jù)庫連接,避免了數(shù)據(jù)丟失和錯(cuò)誤。 記住替換示例代碼中的數(shù)據(jù)庫連接字符串和驅(qū)動(dòng)。 如果數(shù)據(jù)庫操作時(shí)間不可預(yù)測,可能需要更復(fù)雜的機(jī)制來等待所有Goroutine完成,例如使用channel進(jìn)行信號通知。