Goroutine并行执行确认

时间:2017-03-12 20:56:14

标签: go goroutine channels

我是goroutines,频道和喜欢的新手,如果这看似微不足道,那么道歉。

我写了以下代码:

for _, h := range hosts {

      go func() {
        httpClient := cleanhttp.DefaultPooledClient()

        // format the URL with the passed host and por
        url := fmt.Sprintf("https://%s:%v", h.Name, h.Port)
        // create a vault client
        client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
        if err != nil {
          panic(err)
        }
        // get the current status
        status := v.VaultStatus(client)

        // send the status to a channel
        s <- strconv.FormatBool(status.Ready)

      }()

      // assign the value of channel to a var
      cs := <-s

      // print it
      fmt.Printf("Host: %s Status:  %s\n", h.Name, cs)

    }
  }, 

这个想法很简单,它需要一个主机列表,然后使用Golang Vault API来确定当前状态。我很高兴它有效。

我想做的是确保这些操作并行发生。当我运行以下代码时,我得到如下结果:

host: Host1: status: true
host: Host2: status: false
host: Host3: status: true
host: Host4: status: true

这里的问题是这些主机总是以相同的顺序返回。我认为goroutine根本不是并行执行的,因为它们似乎是一个接一个地操作,然后每次都以相同的顺序打印。

代码是否按照我的想法行事?我如何知道这个goroutine并行运行?

3 个答案:

答案 0 :(得分:3)

您一次只运行一个goroutine,因为主循环正在等待通道,然后继续循环的下一次迭代。相反,您应该在所有goroutine启动后等待for循环之外的通道上的结果。顺便说一句,您还需要在频道上发送识别主机的内容。

顺便说一句,你的goroutine功能存在潜在的问题。您正在使用变量h,每次循环都会被主goroutine更改,因此您不会真正了解您在其他goroutine中获得的内容(假设你照顾我上面提到的问题,以便goroutines并行运行)。您应该将它作为参数传递给goroutine函数(或者您可以在for循环中创建一个不同的变量并为其赋值h并在函数内部使用该变量),而不是直接引用该变量。

尝试这样做:

var wg sync.WaitGroup
for _, h := range hosts {
    h := h // create local copy of loop var
    wg.Add(1)
    go func() {
        defer wg.Done()
        httpClient := cleanhttp.DefaultPooledClient()

        // format the URL with the passed host and por
        url := fmt.Sprintf("https://%s:%v", h.Name, h.Port)
        // create a vault client
        client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
        if err != nil {
            panic(err)
        }
        // get the current status
        status := v.VaultStatus(client)

        // print it
        fmt.Printf("Host: %s Status:  %v\n", h.Name, status.Ready)

    }()
}
wg.Wait()

答案 1 :(得分:1)

一般来说,如果你想知道goroutines是否并行运行,你应该trace the scheduler

答案 2 :(得分:1)

假设您有:

type Status struct {
  URL   string
  Ready bool
}

s初始化为:

s := make(chan Status)

然后你可以写:

var wg sync.WaitGroup
for _, h := range hosts {
  h := h
  wg.Add(1)
  go func() {
    defer wg.Done()
    httpClient := cleanhttp.DefaultPooledClient()

    // format the URL with the passed host and por
    url := fmt.Sprintf("https://%s:%v", h.Name, h.Port)
    // create a vault client
    client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
    if err != nil {
      panic(err)
    }
    // get the current status
    status := v.VaultStatus(client)

    // send the status to the channel
    s <- Status{url, status.Ready}

  }()
}
// this goroutine's job is closing s after all above goroutines have finished
go func() {
  wg.Wait()
  close(s) // so the following loop does not block after reading all statuses
}()
for st := range s {
    // here you could collect all statuses in a []Status or something
    // for simplicity, just print them as you did
    fmt.Printf("Host: %s Status: %v\n", st.URL, st.Ready)
}