Search code examples
gogoroutinechannel

Goroutine parallel execution confirmation


I'm new to goroutines, channels and the likes so apologies if this seems trivial.

I've written the following code:

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)

    }
  }, 

The idea is simple, it takes a list of hosts and then uses the Golang Vault API to go and determine the current status. I'm happy enough that it works.

What'd I'd like to do is ensure these operations happen in parallel. When I run the following code, I get the results as follows:

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

The issue here is that these hosts are always returned in the same order. I don't think the goroutines are executing in parallel at all, as they appear to operate one after the other and then get printed in the same order every time.

Is the code doing what I think it should? How can I know this goroutine is operating in parallel?


Solution

  • You are only running one goroutine at a time, because the main goroutine is waiting on the channel before continuing with the next iteration of the loop. Instead, you should wait for the results on the channel outside the for loop after all the goroutines have been started. By the way, you'll need to send something identifying the host on the channel as well.

    By the way, you have a potential problem in your goroutine function. You're using the variable h, which is being changed by the main goroutine each time through the loop, so you don't really know what you're getting in the other goroutines (assuming you take care of the problem I mentioned above so that the goroutines do run in parallel). Instead of referencing that variable directly, you should pass it as an argument to the goroutine function (or you can create a different variable inside the for loop and assign it the value of hand use that variable inside the function).

    Try doing it like this:

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