괴발개발 성장기

Study/Go 언어

[golang] 고루틴 주의 사항

지니유 2022. 10. 11. 18:13
반응형

 

 

# 주의 사항 1)

  • wg.Add(1) 위치가 중요하다
  • 고루틴을 실행하기 전에 wg.Add(1) 를 실행야한다. waitGroup의 초기화를 로직 처리 전에 해줘야 타이밍 이슈를 피할 수 있다.
  • 동시성 이슈가 있어서 add는 고루틴 실행 전에 한꺼번에 초기화를 해주는 것이 좋다.

 

# 문제 발생

고루틴 안에서 add하고 done한다.

func BadExWaitGroup() {
   fmt.Println("나쁜 예시 시작")
   size := 10
   var wg sync.WaitGroup
   for i := 0; i < size; i++ {
      go func() {
         wg.Add(1)
         fmt.Printf("진행 중 %d \n", i)
         wg.Done()
      }()
   }
   wg.Wait()

   fmt.Println("나쁜 예시 끝")
}

결과

나쁜 예시 시작
나쁜 예시 끝

고루틴2로 들어가기 전에 wg가 0이 되어서 main은 끝났다고 인식한다. 그래서 고루틴2가 실행 되지 않고 종료가 됐다.

 

 

# 해결 방안

add를 10으로 하고 고루틴이 돈다. done이 10번 되야지 끝난다.

func GoodExWaitGroup() {
   fmt.Println("좋은 예시 시작")
   size := 10
   var wg sync.WaitGroup
   wg.Add(size)
   for i := 0; i < size; i++ {
      go func() {
         fmt.Printf("진행 중 %d \n", i)
         wg.Done()
      }()
   }
   wg.Wait()

   fmt.Println("좋은 예시 끝")
}

결과

좋은 예시 시작
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
진행 중 10 
좋은 예시 끝

 

위에 문제점만 해결이 되었다. 똑같은 수만 출력이 된다.

 

# 주의 사항 2)

  • 변수를 조심히 해야한다.

 

# 문제점

위에 코드의 예상값은 아래와 같을 것이다. 하지만 실제 값은 같은 수가 나올 때도 있고 다른 수가 나올 때도 있다. 수가 순서대로 나오지는 않는다.

 

# 예상값

좋은 예시 시작
진행 중 0
진행 중 1 
진행 중 2 
진행 중 3 
진행 중 4 
진행 중 5
진행 중 6 
진행 중 7 
진행 중 8 
진행 중 9 
좋은 예시 끝

i = 0일 때 고루틴이 생성해서 새로운 고루틴을 만들었다. 메인에서는 i가 1일 때로 이동한다. 고루틴에서 i를 찾는 시점은 이미 i가 변경 된 후 이다. i가 0이라고 보장을 할 수 없다.

이걸 해결하기 위한 방안 2가지가 있다.

 

해결방안 1)

  • golang 문법 이용
  • i := i로 다시 정의 해준다
fmt.Println("해결책 one 시작")
size := 10
var wg sync.WaitGroup
wg.Add(size)
for i := 0; i < size; i++ {
   i := i  //여기 추가
   go func() {
      fmt.Printf("진행 중 %d \n", i)
      wg.Done()
   }()
}
wg.Wait()
fmt.Println("해결책 one  끝")

 

해결방안2)

  • 고루틴 함수 내에서 사용하는 변수
  • 고루틴을 생성하는 시점의 값을 넣어준다.
  • 고루틴 함수에 i값을 넣어 준다. i값이 고루틴함수에 들어 왔기 때문에 i는 변하지 않고 고루틴 안에서만 유지할 수 있다.
func SolutionTwo() {
   fmt.Println("해결책 two 시작")
   size := 10
   var wg sync.WaitGroup
   wg.Add(size)
   
   for i := 0; i < size; i++ {
  
      go func(i int) {
         fmt.Printf("진행 중 %d \n", i)
         wg.Done()
      }(i)
   }
   wg.Wait()

   fmt.Println("해결책 two 끝")
}

 

 

# 참조

https://github.com/YooGenie/go-study/issues/56

 

고루틴 주의사항 · Issue #56 · YooGenie/go-study

 

github.com

 

 

 

반응형