当我通过循环创建goroutine时,为什么会发生这种情况?

时间:2017-12-07 04:29:39

标签: go goroutine

请帮帮我。 我有块代码,它使用迭代来获取地图的元素,并使用这个元素在Linux机器上的端口上创建一个监听器,但它的执行超出了我的预期。 代码如下所示:

var srvs = map[string]struct {
  id       int
  timezone string
  connCfg  string
  conn     net.Conn
}{"BrazilEastSrv": {id: 1, timezone: "Brazil/East", connCfg: "127.0.0.1:9007"},
  "AustraliaDarwinSrv": {id: 2, timezone: "Australia/Darwin", connCfg: "127.0.0.1:9008"}}

var ch1 = make(chan int, 2)

func main() {
  for k, srv := range srvs {
    go func() {
      if ln, err := net.Listen("tcp", srv.connCfg); err != nil {
        log.Fatal(fmt.Sprintf("%s : %s conn failed,", k, srv.connCfg))
      } else {
        log.Println(fmt.Sprintf("%s:%s created, port:%s listened.", k, srv.connCfg[:9], srv.connCfg[10:]))
        if conn, err := ln.Accept(); err != nil {
          log.Fatal(srv.connCfg, " conn create fatal, errmsg=", err)
        } else {
          srv.conn = conn
          defer srv.conn.Close()
          log.Println("Accepted an access request from cli:", srv.conn.RemoteAddr(), ".")
          handleMsg(srv)
        }
      }
    }()
  }
  var brazilEastSrv, australiaDarwinSrv = <-ch1
  log.Println(brazilEastSrv, australiaDarwinSrv)
}

输出:

2017/12/07 11:45:48 AustraliaDarwinSrv:127.0.0.1 created, port:9008 listened.
2017/12/07 11:45:48 AustraliaDarwinSrv : 127.0.0.1:9008 conn failed,
exit status 1

从输出内容看。它使用迭代的最后一个元素并重用这个元素,为什么会出现这个问题?

但是当我做出一些改变时:

var srvs = map[string]struct {
  id       int
  timezone string
  connCfg  string
  conn     net.Conn
}{"BrazilEastSrv": {id: 1, timezone: "Brazil/East", connCfg: "127.0.0.1:9007"},
  "AustraliaDarwinSrv": {id: 2, timezone: "Australia/Darwin", connCfg: "127.0.0.1:9008"}}

var ch1 = make(chan int, 2)

func main() {
  for k, srv := range srvs {
    go func(srv struct {                                                                                                                                                                   
      id       int
      timezone string
      connCfg  string
      conn     net.Conn
    }) {
      //go func() {
      if ln, err := net.Listen("tcp", srv.connCfg); err != nil {
        log.Fatal(fmt.Sprintf("%s : %s conn failed,", k, srv.connCfg))
      } else {
        log.Println(fmt.Sprintf("%s:%s created, port:%s listened.", k, srv.connCfg[:9], srv.connCfg[10:]))
        if conn, err := ln.Accept(); err != nil {
          log.Fatal(srv.connCfg, " conn create fatal, errmsg=", err)
        } else {
          srv.conn = conn
          defer srv.conn.Close()
          log.Println("Accepted an access request from cli:", srv.conn.RemoteAddr(), ".")
          handleMsg(srv)
        }
      }
      //}()
    }(srv)
  }
  var brazilEastSrv, australiaDarwinSrv = <-ch1
  log.Println(brazilEastSrv, australiaDarwinSrv)
}

它符合我的期望,是什么导致了这种差异?谢谢你的解释。

1 个答案:

答案 0 :(得分:0)

在第一个版本中,你的goroutine使用变量srv(和k),它随着循环的每次迭代而变化,所以你不知道你的goroutine会看到什么值。执行。在第二个版本中,您将srv作为参数传递给goroutine,因此goroutine中的变量srv不再是循环中使用的变量。