문제의 역사:
채널(chan)은 고루틴 간 데이터 교환 및 동시 프로세스 동기화를 위한 중요한 도구로, 이는 Go가 다른 언어와 확실히 차별화되는 점입니다. 채널은 스레드 안전한 데이터 전송을 위해 설계되었습니다.
문제:
채널의 구조를 올바르게 이해하지 못하면 개발자는 종종 데드락, 예상치 못한 차단, 채널 종료 시의 명확하지 않은 오류 및 데이터 손실(경쟁 조건)을 경험하게 됩니다.
해결책:
채널을 선언하고, 사용하고, 닫는 방법을 명확히 이해해야 합니다. 버퍼링된/비버퍼링된 채널을 언제 사용할지 결정하고, 누가 언제 채널을 닫아야 하는지 주의 깊게 살펴봐야 합니다. "모든 것이 멈추는 문제"를 피해야 합니다.
코드 예제:
func producer(ch chan<- int) { // 전송 전용 for i := 0; i < 5; i++ { ch <- i } close(ch) } func consumer(ch <-chan int) { // 읽기 전용 for v := range ch { fmt.Println(v) } } func main() { ch := make(chan int) go producer(ch) consumer(ch) }
주요 특징:
닫힌 채널에 쓰려고 하면 어떤 일이 발생합니까?
패닉이 발생합니다. 독자가 채널을 닫거나, 채널을 닫은 후에도 계속 쓰도록 허용하는 것은 비교적 흔한 실수입니다.
안전하게 채널이 닫혔는지 알 수 있습니까?
아니요, 직접적인 검사는 없습니다. 채널 수명의 감시를 올바르게 설계하거나 읽을 때 두 번째 매개변수(v, ok := <-ch)를 사용해야 하며, 이 때 채널이 닫히면 false가 됩니다.
채널 자체가 스레드 안전합니까?
채널은 고루틴 간 데이터 전송에 대해 스레드 안전합니다. 그러나 여러 고루틴이 하나의 채널에 쓰면 조정 없이 문제를 초래할 수 있습니다(조기 종료 가능성).
여러 생산자와 한 소비자가 있는 프로젝트: 각 생산자가 채널을 닫아서 최종적으로는 패닉이 발생하고 데이터가 손실되었습니다.
장점:
단점:
채널을 닫는 데 오직 한 스레드만 사용하고, 생산자는 별도의 WaitGroup을 통해 작업 완료를 신호했습니다. 채널은 결과 전송 전용으로 사용되었습니다.
장점:
단점: