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