【问题标题】:Goroutine parallel execution confirmationGoroutine 并行执行确认
【发布时间】:2017-03-12 20:56:13
【问题描述】:

我是 goroutines、channels 等的新手,如果这看起来微不足道,我深表歉意。

我写了以下代码:

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 是并行运行的?

【问题讨论】:

    标签: go goroutine channels


    【解决方案1】:

    您一次只运行一个 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()
    

    【讨论】:

    • 我当然可以将它作为参数传递给 goroutine,我一定会更改代码。我添加了通道,因为 main 函数在 goroutines 有时间完成之前就退出了。我应该在这里做什么?游乐场链接会很棒,我只有在看到它们实时可视化时才能理解这些概念:(
    • 如果在 goroutine 函数中打印状态怎么样?要等待 goroutine 完成,您可以使用 sync.WaitGroup。我会把这个放在我的答案中。
    • 太棒了,正是我想要的!我将接受答案,但是,如果您能解释一下您的评论,那就太好了:“相反,在所有 goroutine 启动后,您应该在 for 循环外的通道上等待结果”以及如何做也是这样!
    • 如果您在循环内的通道上等待结果,您将与您创建的 goroutines 同步运行。换句话说,您将启动一个 goroutine 并等待它在通道上发送结果,然后再启动下一个 goroutine,因此它们将一次运行一个。在for循环之后,您可以在for循环中读取通道,该循环为每个主机执行一次。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      假设你有一个:

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

      【讨论】:

        猜你喜欢
        • 2018-08-12
        • 2018-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多