프로그래밍중급 백엔드 Go 개발자

Go에서 채널을 통한 값 전송 시 어떤 일이 발생하나요: 복사는 어떻게 이루어지며, 어떤 유형이 값으로 전송되며, 복잡한 구조체나 포인터를 전달할 때 어떻게 우발적으로 경쟁 상태를 발생시킬 수 있나요?

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

답변

Go에서는 int, struct, 포인터, 인터페이스 등 모든 유형의 값을 채널을 통해 보낼 수 있습니다.

  • "값으로 전송": 표준 타입과 구조체(포인터 없이)는 복사되어 수신자는 자신의 복사본을 받으며, 변경 사항은 원본에 영향을 미치지 않습니다.
  • 경쟁 상태 확인: 만약 채널을 통해 포인터가 전송되면, 양쪽(송신자와 수신자)은 동일한 메모리 영역에서 작업하게 되어 data race가 발생할 수 있습니다!
  • 내부 포인터가 있는 복잡한 구조체: 기본 구조체가 값으로 전송되더라도, 내부 포인터는 참조로 복사되므로 내부 객체 수준에서의 경쟁이 가능해집니다.

코드 및 예제:

type Data struct { N int } c := make(chan Data) d := Data{N: 1} c <- d // 전체 구조체가 복사됨 p := &Data{N: 3} c2 := make(chan *Data) c2 <- p // 채널을 통해 같은 객체에 대한 포인터가 전송됨

속임수 질문

채널을 통해 포인터 필드가 포함된 구조체를 전달하면 - 채널의 양쪽에서 이 필드를 변경할 때 경쟁 상태가 발생할까요?

답변:

  • 경쟁이 가능함! 구조체는 복사되지만, 내부 포인터는 동일한 메모리 영역을 참조합니다. 양쪽 모두가 참조에 의해 데이터를 수정하면 경쟁 상태가 발생합니다.

예제:

type Box struct { Ptr *int } x := 10 chanBox := make(chan Box) chanBox <- Box{Ptr: &x} // 송신자와 수신자 모두 x에 접근할 수 있음!

주제에 대한 미세한 차이로 인한 실제 오류 사례


이야기

분산 큐에서 자주 "우연한" 실패와 작업 구조체의 불가사의한 값들이 발생했습니다. 채널을 통해 공유 구조체에 대한 포인터가 여러 고루틴에서 동시에 변경되는 것을 발견했습니다. 데이터 복사로 전송 방식을 변경하기로 결정했습니다.


이야기

비동기 메시지 처리는 내부에 공유 배열에 대한 슬라이스 포인터를 가진 구조체들을 다루고 있었습니다. 같은 메모리의 일부가 서로 다른 위치에서 동시에 변경되어 숨겨진 버그와 데이터 손상을 초래했습니다.


이야기

푸시 알림 서비스에서는 채널을 통해 객체에 대한 참조가 전달되었고, 이후 고루틴에서 처리되었습니다. 작업 완료 시점과 채널 닫기가 동시에 발생하면서 일부 구조체가 여전히 수정되어 패닉이나 data race를 유발했습니다. 전송 전에 구조체를 복사하도록 변경하여 도움이 되었습니다.