프로그래밍백엔드 개발자

Go에서 클로저가 어떻게 구현되었으며, 루프 내부에서 고루틴을 실행할 때 사용과 관련된 잠재적인 문제점은 무엇이며 전형적인 실수를 피하는 방법은 무엇입니까?

Hintsage AI 어시스턴트로 면접 통과

응답

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

주제에 대한 지식 부족으로 인한 실제 오류 사례


이야기

상품 분석 시스템에서 데이터가 클로저를 사용하여 루프 변수를 캡처하여 병렬 고루틴에서 익명화되었습니다. 결과적으로 모든 병렬 작업이 동일한 데이터 세트를 처리하여 통계 왜곡과 잘못된 보고서가 발생했습니다.

이야기

클라우드 서비스의 Go 통합 백엔드 팀은 메트릭 수집을 최적화하기 위해 익명 함수로 처리하도록 루프를 실행했으며, 고루틴 내부에서 맵의 인덱스를 캡처했습니다. 결과적으로 일부 핸들러는 자신의 서비스가 아닌 마지막으로 처리된 인덱스에 대한 데이터를 수집했습니다.

이야기

배달 스타트업에서 주문 좌표 업데이트 시 클로저를 잘못 사용한 결과, 여러 번에 걸쳐 마지막 주문의 좌표만 업데이트되고 현재 좌표가 아니라 고루틴 내부에서 슬라이스에 접근할 때 경쟁 상태가 발생했습니다.