sync.WaitGroup의 예가 맞습니까? 고 루틴을 한 번에

이 예제 사용법이 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")
}

답변