将参数传递给函数闭包

时间:2015-05-12 06:46:14

标签: go closures

我试图理解创建一个带参数的匿名函数与将该函数作为闭包之间的Go的区别。这是差异的一个例子。

使用参数:

func main() {
  done := make(chan bool, 1)
  go func(c chan bool) {
    time.Sleep(50 * time.Millisecond)
    c <- true
  }(done)
  <-done
}

作为封闭:

func main() {
  done := make(chan bool, 1)
  go func() {
    time.Sleep(50 * time.Millisecond)
    done <- true
  }()
  <-done
}

我的问题是,第一种形式何时优于第二种形式?你有没有使用参数这种东西?我唯一能看到第一种形式有用的是从另一个函数返回func(x, y)时。

3 个答案:

答案 0 :(得分:15)

使用闭包与使用函数参数之间的区别与共享相同的变量与获取值的副本有关。请考虑以下两个例子。

Closure 中,所有函数调用都将使用i中存储的值。在任何goroutine有时间打印它的值之前,这个值很可能已经达到3。

参数示例中,每次函数调用都会在调用时传递i值的副本,从而为我们提供更可能需要的结果:

<强>关闭:

for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i)
    }()
}

结果:

  

3
  3
  3

<强>参数:

for i := 0; i < 3; i++ {
    go func(v int) {
        fmt.Println(v)
    }(i)
}

结果:

  

0
  1
  2

游乐场: http://play.golang.org/p/T5rHrIKrQv

答案 1 :(得分:5)

何时使用参数

如果您计划更改您不希望在函数中观察的变量的值,那么首选表格是首选。

这是典型的情况,当匿名函数在for循环内并且您打算使用循环的变量时,例如:

for i := 0; i < 10; i++ {
    go func(i int) {
        fmt.Println(i)
    }(i)
}

如果不传递变量i,您可能会看到打印10十次。通过i后,您会看到从09打印的数字。

何时不使用参数

如果您不想更改变量的值,则不传递它更便宜,因此不会创建它的另一个副本。对于大型结构尤其如此。虽然如果您以后更改代码并修改变量,您可能很容易忘记检查其对闭包的影响并获得意外结果。

同样可能存在想要观察对&#34;外部&#34;所做的更改的情况。变量,例如:

func GetRes(name string) (Res, error) {
    res, err := somepack.OpenRes(name)
    if err != nil {
        return nil, err
    }

    closeres := true
    defer func() {
        if closeres {
            res.Close()
        }
    }()

    // Do other stuff
    if err = otherStuff(); err != nil {
        return nil, err // res will be closed
    }

    // Everything went well, return res, but
    // res must not be closed, it will be the responsibility of the caller
    closeres = false

    return res, nil // res will not be closed
}

在这种情况下,GetRes()将打开一些资源。但在返回之前,必须完成其他可能也会失败的事情。如果失败,则必须关闭res并且不返回。如果一切顺利,res不得关闭并返回。

答案 2 :(得分:0)

这是来自 net/Listen 的参数示例

package main

import (
    "io"
    "log"
    "net"
)

func main() {
    // Listen on TCP port 2000 on all available unicast and
    // anycast IP addresses of the local system.
    l, err := net.Listen("tcp", ":2000")
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()
    for {
        // Wait for a connection.
        conn, err := l.Accept()
        if err != nil {
            log.Fatal(err)
        }
        // Handle the connection in a new goroutine.
        // The loop then returns to accepting, so that
        // multiple connections may be served concurrently.
        go func(c net.Conn) {
            // Echo all incoming data.
            io.Copy(c, c)
            // Shut down the connection.
            c.Close()
        }(conn)
    }
}