이번 강의에서 배울 채널에서의 select문은 '분기문' 챕터에서 학습한 switch문과 거의 흡사한 용법으로 사용됩니다. switch문은 어떠한 조건이 주어졌을 때, 그 조건에 맞는 부분을 실행합니다. 쉽게 말해서, 실행 흐름을 제어하는 것입니다. switch문을 학습한지 오래되어 기억하지 못한다면 아래 코드를 바로 실행해보세요.
콘솔에 1, 2, 3을 순서대로 출력하는 것이 아니라 num
은 3이기 때문에 case 3:
에 의해 fmt.Println("3")
만 실행합니다. 이와 마찬가지로 채널 데이터를 수신하거나 송신할 때 select문을 사용하면 먼저 송신이 된 채널을 수신하고 해당 부분을 실행하거나 채널로 데이터를 송신할 수 있습니다.
select문을 사용하기 전에 지금까지 사용해왔던 일반적인 흐름의 채널 송/수신 예시 코드를 바로 실행해보세요.
위 코드에서는 총 4개의 루틴이 있습니다.
ch1
,ch2
채널을 생성하고 3초 기다리는 main 루틴- 1000밀리초를 대기하고
ch1
에 'true'를 송신하는 것을 반복하는 고루틴 - 500밀리초를 대기하고
ch2
에 'true'를 송신하는 것을 반복하는 고루틴 ch1
수신, "ch1 수신" 출력,ch2
수신, "ch2 수신" 출력을 순서대로 반복하는 고루틴
이제는 클로저의 사용이 익숙할 것이라고 생각합니다. 클로저는 main 루틴에서 생성한 채널들을 매개변수 없이 접근합니다. 채널 ch1
과 ch2
에 데이터를 송신하는 두 고루틴은 각 채널에 데이터를 송신하기 전에 서로 다른 대기 시간을 가집니다. 데이터를 수신하는 루틴은 ch1
, ch2
순서로 데이터를 수신합니다. 이때, 고루틴이 모두 동시(Concurrency)에 시작되어 ch2
채널이 ch1
채널보다 데이터가 먼저 송신돼도 수신 루틴에서 ch1
채널을 먼저 수신하기 때문에 ch1
채널을 수신할 때까지 기다려야합니다. ch1
채널 데이터가 1000밀리초만에 수신되면 이미 송신을 마친 ch1
채널 데이터가 수신됩니다. 이 과정을 반복하면 결국 ch1
채널과 ch2
채널은 동일하게 1000밀리초를 기다리는 것입니다.
분명히 ch2
채널이 이미 송신 되었음에도 불구하고 ch1
채널이 송신되는 것을 기다리는 것은 굉장히 효율적이지 않습니다. 따라서 이러한 상황에 송신된 채널을 선택적으로 처리하는 select문을 사용해 채널 데이터 송/수신 효율을 높일 수 있습니다. 채널 수신에 있어서 select문의 형태를 먼저 알아보겠습니다.
switch
와 다르게 select
뒤에 따로 검사할 변수나 식을 두지 않습니다. case
의 수신자 채널이 송신되면 해당 case
를 실행합니다. 위 코드를 select문으로 수정해보겠습니다.
아래 코드를 바로 실행해보세요.
수신 루틴에서 select문을 사용했기 때문에 ch2
채널은 ch1
채널에 데이터가 송신될 때까지 기다리지 않고 송신되면 바로 select문에 의해 바로 수신됩니다. 그리고 switch문에서 case 뒤에 표현식을 사용할 수 있었던 것처럼 select문에서는 "case 변수 := <- 채널:" 형식으로 데이터 수신과 동시에 변수에 데이터를 초기화할 수 있습니다. 조금만 수정해 위 예시에 바로 적용해보겠습니다.
맨 위에서 언급한 것처럼 select문은 송신된 채널이 있을 때, 그 채널을 수신하는 기능을 하는 것 뿐만이 아니라 case를 이용해 채널에 데이터를 송신할 수 있습니다. select문에 채널에 데이터를 송신하는 case가 있다면 항상 데이터를 송신하고, 채널에 데이터가 수신됐다면 데이터를 받는 case가 실행됩니다. 쉽게 말해서 송신자와 수신자가 모두 select문에 있을때 송신된 데이터가 없으면 계속 송신자 case를 실행해 데이터를 송신합니다.
아래 예시 코드를 바로 실행해보세요.
콘솔에 출력되는 것을 확인해보면, ch1과 ch2의 채널에 데이터가 송신되지 않는 대기 시간 중간에 case ch3 <- "goorm":
으로 데이터를 송신하고 200밀리초 후에 데이터를 수신하는 것을 알 수 있습니다.