gpt4 book ai didi

go - WaitGroup 和同步在 Go 中不起作用

转载 作者:行者123 更新时间:2023-12-01 22:22:31 25 4
gpt4 key购买 nike

我一直在试验 goroutine 和 channel ,我想测试 WaitGroup 功能。在这里,我试图执行一个 HTTP 泛洪作业,其中父线程会产生许多 goroutines,这些 goroutines 将发出无限请求,除非收到停止消息:

func (hf *HTTPFlood) Run() {
childrenStop := make(chan int, hf.ConcurrentCalls)
stop := false

totalRequests := 0
requestsChan := make(chan int)
totalErrors := 0
errorsChan := make(chan int)

var wg sync.WaitGroup
for i := 0; i < hf.ConcurrentCalls; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-childrenStop:
fmt.Printf("stop child\n")
return
default:
_, err := Request(hf.Victim.String())
requestsChan <- 1
if err != nil {
errorsChan <- 1
}
}
}
}()
}
timeout := time.NewTimer(time.Duration(MaximumJobTime) * time.Second)
for !stop {
select {
case req := <- requestsChan:
totalReq += req
case err := <- errorsChan:
totalErrors += err
case <- timeout.C:
fmt.Printf("%s timed up\n", hf.Victim.String())
for i := 0; i < hf.ConcurrentCalls; i++ {
childrenStop <- 1
}
close(childrenStop)
stop = true
break
}
}
fmt.Printf("waiting\n")
wg.Wait()
fmt.Printf("after wait\n")
close(requestsChan)
close(errorsChan)
fmt.Printf("end\n")
}

一旦触发超时,父线程成功退出循环并到达等待指令,但即使 stopChildren channel 已满,子 goroutine 似乎永远不会在 stopChildren channel 上收到消息。

我错过了什么?

编辑:

所以问题显然是如何管理 channel 及其发送/接收。
首先,在所有 child 收到消息之前,childrenStop channel 已关闭。等待后 channel 应关闭

另一方面,由于一旦父线程发送停止信号后,requestChan 和 errorsChan 都没有进行读取,因此大多数子线程在这两个 channel 上的发送都处于阻塞状态。我试图在等待之前的循环之外继续读取父线程,但这不起作用,所以我将实现切换到原子计数器,这似乎是管理这个特定用例的更合适的方式。
func (hf *HTTPFlood) Run() {
childrenStop := make(chan int, hf.ConcurrentCalls)

var totalReq uint64
var totalErrors uint64

var wg sync.WaitGroup
for i := 0; i < hf.ConcurrentCalls; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-childrenStop:
fmt.Printf("stop child\n")
return
default:
_, err := Request(hf.Victim.String())
atomic.AddUint64(&totalReq, 1)
if err != nil {
atomic.AddUint64(&totalErrors, 1)
}
}
}
}()
}
timeout := time.NewTimer(time.Duration(MaximumJobTime) * time.Second)

<- timeout.C
fmt.Printf("%s timed up\n", hf.Victim.String())
for i := 0; i < hf.ConcurrentCalls; i++ {
childrenStop <- 1
}
fmt.Printf("waiting\n")
wg.Wait()
fmt.Printf("after wait\n")
close(childrenStop)
fmt.Printf("end\n")

}

最佳答案

您的 goroutine 可以在 requestsChan <- 1 被阻止.

case <- timeout.C:
fmt.Printf("%s timed up\n", hf.Victim.String())
for i := 0; i < hf.ConcurrentCalls; i++ {
childrenStop <- 1
}
close(childrenStop)
stop = true
break

在这里,您将号码发送到 childrenStop并期望 go 例程接收它。但是当您发送 childrenStop 信号时,您的例程可能会在 requestsChan 上发送一些内容。 .但是当您在发送关闭信号后从循环中中断时,没有人在监听 requestsChan接受。

您可以通过在 requestsChan <- 1 之前和之后打印一些内容来确认这一点。确认行为。

当您在 channel 上发送内容而另一端没有人接收时, channel 将阻塞

这是一个可能的修改。
package main

import (
"fmt"
"time"
)

func main() {
requestsChan := make(chan int)

done := make(chan chan bool)
for i := 0; i < 5; i++ {
go func(it int) {
for {
select {
case c := <-done:
c <- true
return
default:
requestsChan <- it
}
}

}(i)
}

max := time.NewTimer(1 * time.Millisecond)
allChildrenDone := make(chan bool)
childrenDone := 0
childDone := make(chan bool)

go func() {
for {
select {
case i := <-requestsChan:
fmt.Printf("received %d;", i)
case <-max.C:
fmt.Println("\nTimeup")
for i := 0; i < 5; i++ {
go func() {
done <- childDone
fmt.Println("sent done")
}()
}
case <-childDone:
childrenDone++
fmt.Println("child done ", childrenDone)
if childrenDone == 5 {
allChildrenDone <- true
return
}
}
}
}()

fmt.Println("Waiting")
<-allChildrenDone
}

这里要注意的是,我在 go 例程中发送关闭信号,以便循环可以继续,同时我等待所有 child 干净地退出。

请收看 this Rob Pike 的谈话清楚地涵盖了这些细节。

[编辑]:之前的代码在退出后会导致运行例程。

关于go - WaitGroup 和同步在 Go 中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62322457/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com