あなたが持っているのは
リンクされたウィキペディアの記事では、デッドロックの防止について詳しく説明しています。たとえば、コードの実行では、必要な(または必要になる)すべてのリソースがある場合にのみ、クリティカルセクション(リソースをロックする)に入る必要があります。この場合、これは2つの接続を予約する必要があることを意味します(正確に2つ。1つしか使用できない場合はそのままにして待機します)。2つある場合は、クエリを続行します。ただし、Goでは事前に接続を予約することはできません。クエリを実行するときに、必要に応じて割り当てられます。
通常、このパターンは避ける必要があります。最初に(有限の)リソース(この場合はdb接続)を予約するコードを記述しないでください。それを解放する前に、別のリソースを要求します。
簡単な回避策は、最初のクエリを実行し、その結果を保存して(たとえば、Goスライスに)、それが完了したら、後続のクエリに進むことです(ただし、 sql.Rows
最初)。このように、コードは同時に2つの接続を必要としません。
そして、エラーを処理することを忘れないでください!簡潔にするために省略しましたが、コードに含めるべきではありません。
これはどのように見えるかです:
go func() {
defer wg.Done()
rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
var data []int // Use whatever type describes data you query
for rows.Next() {
var something int
rows.Scan(&something)
data = append(data, something)
}
rows.Close()
for _, v := range data {
// You may use v as a query parameter if needed
db.Exec("SELECT * FROM reviews LIMIT 1")
}
}()
rows.Close()
に注意してください defer
として実行する必要があります (パニックの場合でも)実行されることを確認するステートメント。ただし、単にdefer rows.Close()
を使用する場合 、これは後続のクエリが実行された後にのみ実行されるため、デッドロックを防ぐことはできません。したがって、defer
を使用できる別の関数(匿名関数の場合もあります)で呼び出すようにリファクタリングします。 :
rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
var data []int // Use whatever type describes data you query
func() {
defer rows.Close()
for rows.Next() {
var something int
rows.Scan(&something)
data = append(data, something)
}
}()
また、2番目のfor
にも注意してください プリペアドステートメントをループします( sql.Stmt
) DB.Prepare()
によって取得されました
同じ(パラメータ化された)クエリを複数回実行する方がおそらくはるかに良い選択でしょう。
別のオプションは、新しいゴルーチンで後続のクエリを起動して、現在ロックされている接続(または他のゴルーチンによってロックされている他の接続)が解放されたときに実行されるクエリを実行できるようにすることですが、明示的な同期がないと、いつ制御できませんそれらは実行されます。次のようになります:
go func() {
defer wg.Done()
rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
defer rows.Close()
for rows.Next() {
var something int
rows.Scan(&something)
// Pass something if needed
go db.Exec("SELECT * FROM reviews LIMIT 1")
}
}()
プログラムでこれらのゴルーチンも待機させるには、WaitGroup
を使用します。 すでに行動しています:
// Pass something if needed
wg.Add(1)
go func() {
defer wg.Done()
db.Exec("SELECT * FROM reviews LIMIT 1")
}()