Go에서 클로저(closures)는 자신의 주변 범위의 변수를 "잠그는"(캡처하는) 함수입니다. 클로저는 주로 다른 함수 내부에서 생성된 익명 함수에 사용됩니다.
클로저와 작업할 때 가장 흔한 문제는 루프 변수와 함께 고루틴을 사용할 때의 예기치 않은 동작입니다:
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }
각 고루틴은 동일한 i 값을 출력할 수 있습니다. 왜냐하면 변수 i는 반복되고 클로저가 변수의 주소를 캡처하기 때문입니다. 즉, 각 반복의 값이 아니라 변수를 캡처하는 것입니다.
올바른 방법:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
이러한 동작은 클로저가 변수를 참조(주소)로 유지하기 때문에 발생합니다. 즉, 그 값이 아닌 참조를 캡처합니다.
루프 내부에 몇 개의 고루틴이 실행되면 루프 변수를 캡처할 경우 어떤 값을 출력합니까?
답변: 모든 고루틴은 동일한 값(종종 마지막 값)을 출력할 수 있습니다. 왜냐하면 그들은 변수의 현재 값을 보기 때문입니다. 고루틴 실행 시점에 루프는 이미 종료되었습니다. 이를 피하려면 변수를 클로저의 매개변수로 전달해야 합니다.
예시:
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // 대개 결과는: 5 5 5 5 5
이야기
이야기
이야기
배달 스타트업에서 주문 좌표 업데이트 시 클로저를 잘못 사용한 결과, 여러 번에 걸쳐 마지막 주문의 좌표만 업데이트되고 현재 좌표가 아니라 고루틴 내부에서 슬라이스에 접근할 때 경쟁 상태가 발생했습니다.