Goroutine并行执行确认

Goroutine parallel execution confirmation

我是 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

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

代码是否按照我认为应该的方式运行?我如何知道这个goroutine是并行运行的?

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

您一次只能 运行 一个 goroutine,因为主 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()

假设你有一个:

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)
}