이 예제 사용법이 sync.WaitGroup
맞습니까? 예상 된 결과를 제공하지만의 wg.Add(4)
및 위치에 대해 잘 모르겠습니다 wg.Done()
. 4 개의 고 루틴을 한 번에 추가하는 것이 합리적 wg.Add()
입니까?
http://play.golang.org/p/ecvYHiie0P
package main
import (
"fmt"
"sync"
"time"
)
func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
duration := millisecs * time.Millisecond
time.Sleep(duration)
fmt.Println("Function in background, duration:", duration)
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(4)
go dosomething(200, &wg)
go dosomething(400, &wg)
go dosomething(150, &wg)
go dosomething(600, &wg)
wg.Wait()
fmt.Println("Done")
}
결과 (예상대로) :
Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done
답변
예,이 예가 맞습니다. 경합 상태를 방지 wg.Add()
하기 위해 go
문 앞에 발생 하는 것이 중요합니다 . 다음도 정확합니다.
func main() {
var wg sync.WaitGroup
wg.Add(1)
go dosomething(200, &wg)
wg.Add(1)
go dosomething(400, &wg)
wg.Add(1)
go dosomething(150, &wg)
wg.Add(1)
go dosomething(600, &wg)
wg.Wait()
fmt.Println("Done")
}
그러나 wg.Add
이미 몇 번 호출 될 것인지 이미 알고있을 때 계속해서 호출하는 것은 의미가 없습니다.
Waitgroups
카운터가 0 아래로 떨어지면 패닉. 카운터는 0에서 시작하고 각각 Done()
은 a -1
이며 각각 Add()
은 매개 변수에 따라 다릅니다. 그래서, 카운터가 결코 아래로 떨어질없고 피할 패닉 수 있도록, 당신은이 필요로 Add()
하는 보장 전과 와서 Done()
.
Go에서 이러한 보장은 메모리 모델에 의해 제공됩니다 .
메모리 모델은 단일 고 루틴의 모든 명령문이 작성된 순서대로 실행되는 것으로 나타납니다. 실제로 그 순서가 아닐 수도 있지만 결과는 마치 그럴 것입니다. 또한 goroutine은이 go
를 호출 하는 문이 끝날 때까지 실행되지 않습니다 . (가)부터 Add()
전과 발생 go
문과 go
문이 전에 발생 Done()
, 우리는 알고 Add()
전과 발생합니다 Done()
.
이 go
명령문을 앞에 올 Add()
경우 프로그램이 올바르게 작동 할 수 있습니다. 그러나 보장되지 않으므로 경쟁 조건이됩니다.
답변
함수 자체에 wg.Add()
호출을 임베드하는 것이 좋습니다. 호출 doSomething()
횟수를 조정하는 경우 추가 매개 변수를 수동으로 조정하지 않아도됩니다. 이렇게하면 매개 변수를 업데이트하지만 업데이트하는 것을 잊으면 오류가 발생할 수 있습니다. 기타 (이 사소한 예제에서는 가능성이 낮지 만 여전히 코드 재사용을위한 더 나은 방법이라고 생각합니다.)
Stephen Weinberg 가이 질문 에 대한 답변 에서 지적했듯이 gofunc 를 생성 하기 전에 waitgroup을 증가 시켜야하지만 다음 doSomething()
과 같이 함수 자체 내부에 gofunc 생성을 래핑하여 쉽게 수행 할 수 있습니다 .
func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
wg.Add(1)
go func() {
duration := millisecs * time.Millisecond
time.Sleep(duration)
fmt.Println("Function in background, duration:", duration)
wg.Done()
}()
}
그런 다음 호출없이 호출 할 수 있습니다 go
. 예 :
func main() {
var wg sync.WaitGroup
dosomething(200, &wg)
dosomething(400, &wg)
dosomething(150, &wg)
dosomething(600, &wg)
wg.Wait()
fmt.Println("Done")
}
놀이터로 : http://play.golang.org/p/WZcprjpHa_
답변
- Mroth 답변을 기반으로 한 작은 개선
- Done에 대해 지연을 사용하는 것이 더 안전합니다.
func dosomething(millisecs time.Duration, wg *sync.WaitGroup) { wg.Add(1) go func() { defer wg.Done() duration := millisecs * time.Millisecond time.Sleep(duration) fmt.Println("Function in background, duration:", duration) }() } func main() { var wg sync.WaitGroup dosomething(200, &wg) dosomething(400, &wg) dosomething(150, &wg) dosomething(600, &wg) wg.Wait() fmt.Println("Done") }