英文:
How does golang defer work in cycles?
问题
我正在尝试处理与MongoDB的重新连接。为此,我尝试执行每个操作三次(以防它因io.EOF而失败)。
type MongoDB struct {
session *mgo.Session
DB *mgo.Database
}
func (d MongoDB) performWithReconnect(collection string,
operation func(*mgo.Collection) error) error {
var err error
for i := 0; i < 3; i++ {
session := d.session.Copy()
defer session.Close()
err = operation(session.DB(Config.MongoDb).C(collection))
if err == io.EOF {
continue
}
if err == nil {
return err
}
}
return err
}
所以问题是关于defer
语句。它会像我想的那样关闭所有会话吗,还是会有其他行为?如果你知道一些处理这种情况的好方法,我会很高兴阅读它们。
英文:
I'm trying to handle reconnections to MongoDB. To do this I try to perform every operation three times (in case it fails with io.EOF)
type MongoDB struct {
session *mgo.Session
DB *mgo.Database
}
func (d MongoDB) performWithReconnect(collection string,
operation func(*mgo.Collection) error) error {
var err error
for i := 0; i < 3; i++ {
session := d.session.Copy()
defer session.Close()
err = operation(session.DB(Config.MongoDb).C(collection))
if err == io.EOF{
continue
}
if err == nil{
return err
}
}
return err
}
So the question is about defer. Will it close all sessions as I suppose or it is going to behave some other way?
If you know some good practices to handle this different way I will be happy to read them.
答案1
得分: 6
考虑以下程序:
package main
import (
"fmt"
)
func print(s string, i int) {
fmt.Println(s, i)
}
func main() {
for i := 0; i < 3; i++ {
defer print("loop", i)
}
fmt.Println("after loop 1")
for i := 0; i < 3; i++ {
func(i int) {
defer print("func", i)
}(i)
}
fmt.Println("after loop 2")
}
它将打印:
after loop 1
func 0
func 1
func 2
after loop 2
loop 2
loop 1
loop 0
延迟函数调用将被放入堆栈中,并在包围函数结束时按相反的顺序执行。在您的情况下,这将是相当糟糕的,因为您将有等待关闭的连接。
我建议将循环的内容包装在内联函数中。它将按您希望的方式调用延迟函数。
英文:
Consider the following program
package main
import (
"fmt"
)
func print(s string, i int) {
fmt.Println(s, i)
}
func main() {
for i := 0; i < 3; i++ {
defer print("loop", i)
}
fmt.Println("after loop 1")
for i := 0; i < 3; i++ {
func(i int) {
defer print("func", i)
}(i)
}
fmt.Println("after loop 2")
}
It will print
after loop 1
func 0
func 1
func 2
after loop 2
loop 2
loop 1
loop 0
The deferred function calls will be put on stack and then executed in a reverse order at the end of surrounding function. In your case it will be quite bad as you will have connections waiting to be closed.
I recommend wrapping the contents of loop into an inline function. It will call deferred function just as you want it.
答案2
得分: 2
在你的代码中,你创建了三个(相同的)defer函数,它们将在函数退出时运行。
如果你需要在循环内部运行defer
,你必须将其放在一个函数内部。可以通过匿名函数来实现:
for i := 0; i < 3; i++ {
err := func() error {
session := d.session.Copy()
defer session.Close()
return operation(session.DB(Config.MongoDb).C(collection))
}()
if err == io.EOF {
continue
}
if err != nil {
return err
}
}
这样,每次循环时都会创建一个新的函数,并在函数内部使用defer
语句。
英文:
From A Tour of Go:
> A defer statement defers the execution of a function until the surrounding function returns.
So in your code, you're creating three (identical) defer functions, which will all run when the function exits.
If you need a defer
to run inside of a loop, you have to put it inside of a function. This can be done in an anonymous function thusly:
for i := 0; i < 3; i++ {
err := func() error {
session := d.session.Copy()
defer session.Close()
return operation(session.DB(Config.MongoDb).C(collection))
}()
if err == io.EOF {
continue
}
if err != nil {
return err
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论