Goroutine并行执行确认

huangapple go评论85阅读模式
英文:

Goroutine parallel execution confirmation

问题

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

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

这段代码是否按照我所想的那样工作?我如何知道这个goroutine是在并行操作?

英文:

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(&quot;https://%s:%v&quot;, h.Name, h.Port)
        // create a vault client
        client, err := api.NewClient(&amp;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 &lt;- strconv.FormatBool(status.Ready)

      }()

      // assign the value of channel to a var
      cs := &lt;-s

      // print it
      fmt.Printf(&quot;Host: %s Status:  %s\n&quot;, 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?

答案1

得分: 3

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

顺便说一句,你的goroutine函数中存在一个潜在的问题。你正在使用变量h,而主goroutine在每次循环中都会更改它,所以你不知道在其他goroutine中得到的是什么值(假设你解决了我上面提到的问题,使得goroutine可以并行运行)。你应该将它作为参数传递给goroutine函数(或者你可以在for循环内部创建一个不同的变量,并将其赋值为h的值,并在函数内部使用该变量)。

尝试像这样做:

var wg sync.WaitGroup
for _, h := range hosts {
    h := h // 创建循环变量的本地副本
    wg.Add(1)
    go func() {
        defer wg.Done()
        httpClient := cleanhttp.DefaultPooledClient()

        // 使用传递的主机和端口格式化URL
        url := fmt.Sprintf("https://%s:%v", h.Name, h.Port)
        // 创建一个Vault客户端
        client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
        if err != nil {
            panic(err)
        }
        // 获取当前状态
        status := v.VaultStatus(client)

        // 打印状态
        fmt.Printf("主机:%s 状态:%v\n", h.Name, status.Ready)

    }()
}
wg.Wait()
英文:

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(&quot;https://%s:%v&quot;, h.Name, h.Port)
		// create a vault client
		client, err := api.NewClient(&amp;api.Config{Address: url, HttpClient: httpClient})
		if err != nil {
			panic(err)
		}
		// get the current status
		status := v.VaultStatus(client)

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

	}()
}
wg.Wait()

答案2

得分: 1

一般来说,如果你想知道goroutine是否在并行运行,你应该追踪调度器。

英文:

Generally speaking, if you want to know whether goroutines are operating in parallel, you should trace the scheduler.

答案3

得分: 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()

    // 使用传入的主机和端口格式化URL
    url := fmt.Sprintf("https://%s:%v", h.Name, h.Port)
    // 创建一个Vault客户端
    client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
    if err != nil {
      panic(err)
    }
    // 获取当前状态
    status := v.VaultStatus(client)

    // 将状态发送到通道
    s <- Status{url, status.Ready}

  }()
}
// 这个goroutine的任务是在上面的所有goroutine完成后关闭s
go func() {
  wg.Wait()
  close(s) // 这样在读取完所有状态后,下面的循环不会被阻塞
}()
for st := range s {
    // 在这里你可以将所有状态收集到一个[]Status或其他数据结构中
    // 为了简单起见,就像你之前做的那样打印它们
    fmt.Printf("Host: %s Status: %v\n", st.URL, st.Ready)
}
英文:

Assuming you have a:

type Status struct {
  URL   string
  Ready bool
}

And s initialized as:

s := make(chan Status)

Then you could write:

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(&quot;https://%s:%v&quot;, h.Name, h.Port)
    // create a vault client
    client, err := api.NewClient(&amp;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 &lt;- Status{url, status.Ready}

  }()
}
// this goroutine&#39;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(&quot;Host: %s Status: %v\n&quot;, st.URL, st.Ready)
}

huangapple
  • 本文由 发表于 2017年3月13日 04:56:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/42753067.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定