이전 강의에서 채널은 고루틴간의 데이터 송/수신을 위해 존재하는 것이기 때문에 채널을 사용하기 위해서는 고루틴을 꼭 사용해야한다고 했습니다. 채널에서는 데이터 송/수신의 역할을 하는 송신자와 수신자가 있습니다. 송신자는 채널에 데이터를 보내는 역할을 하고(채널 <- 데이터), 수신자는 채널로부터 데이터를 받는 역할을 합니다.(<- 채널) 또한 채널을 사용한 송신 루틴은 수신 루틴이 데이터를 받을 때까지, 수신 루틴은 송신 루틴이 데이터를 보낼 때까지 대기합니다. 따라서 채널을 이용해 비동기 프로세스에서 데이터를 원활하게 주고받을 수 있으며, 고루틴간의 흐름을 제어할 수 있는 것입니다. 이렇게 채널은 비동기 프로세스의 기능을 원활하게 해주고 극대화시킵니다.
채널을 사용할 때 우리가 간과할 수 있는 치명적인 오류가 발생할 수 있는데 바로 '데드락(Deadlock)'입니다. 데드락은 한국말로 '교착 상태'라는 뜻인데 이것에 관해 설명하자면 컴퓨터 구조론 서적의 한 챕터를 설명하는 것과 마찬가지기 때문에 채널 사용에서 발생하는 상황만 설명하겠습니다. 데드락은 둘 이상의 프로세스(함수)가 서로 가진 한정된 자원을 요청하는 경우 발생하는 것으로, 프로세스가 전진되지 못하고 모든 프로세스가 대기 상태가 되는 것을 말합니다. [출처 : 네이버 지식백과] 채널을 사용할 때는 main() 함수에서 고루틴이 무한 대기 상태가 됐을 때 데드락이 발생합니다. 쉽게 말해서, main() 함수에서 송/수신 채널이 대기 상태가 되면 프로그램이 진행되지 않아 종료되지 않는 것입니다.
아래 수신자가 없어 무한 대기로 데드락이 발생하는 코드를 바로 실행해보세요.
위 코드에서 c
라는 string형 채널을 생성하고 데이터를 보내고있는데 데이터를 받는 수신자(수신 루틴)가 없기 때문에 값을 수신할 때까지 무한 대기하는 데드락이 발생하는 것입니다. 그래서 송/수신을 위한 고루틴을 만들고 수신자와 송신자의 요건을 충족시키면 데드락 상황이 발생하지 않고 프로그램이 실행됩니다.
비동기 채널 버퍼
이때, 채널에서 송/수신이 꼭 일대일 대응을 해야하기때문에 좀 번거로운 상황이 생길 수 있습니다. 그래서 이를 중재하는 역할을 하는 '버퍼'라는 것이 있습니다. 송신 루틴에서 수신 루틴으로 데이터를 바로 전달하는 것이 아니라 특정 개수의 버퍼를 만들어 송신자는 버퍼로 데이터를 보내고, 수신자는 버퍼에서 데이터를 가져오게끔 합니다. 쉽게 말해서, 송/수신자를 연결하는 통로 중간에 데이터를 잠깐 저장할 수 있는 공간을 마련하는 것입니다. 버퍼를 만드는 형식은 "make(chan 데이터타입, 버퍼 개수)" 입니다.
여기서 주의할 점이 있는데 쉽게 기억하는 방법이 있습니다. 송신자와 수신자는 서로 사이가 안좋아 자기가 할 일만 하면 끝이라고 생각하는 것입니다. 따라서 송신 루틴은 수신자가 없어도 버퍼에 보내면 일을 끝내고, 수신 루틴은 일단 값을 받으면 송신 루틴의 일이 끝나든 아니든 자신의 일을 끝냅니다.
아래 채널 버퍼를 만들어 수신 루틴이 없어도 버퍼에 값을 보내 오류 없이 프로그램이 종료되는 예시 코드를 바로 실행해보세요.
비동기 채널 버퍼에서 고루틴의 대기 조건을 정리해보겠습니다.
- 송신 루틴은 버퍼가 가득차면 대기합니다.
- 보내고 할 일을 함. 보낸 순간 버퍼가 가득찼으면 대기, 버퍼에 빈 공간이 생기면 하던 일 마저 끝냄.
- 수신 루틴은 버퍼에 값이 없으면 대기합니다.(버퍼에 값이 들어올 때까지)
아래 채널 버퍼링을 이용한 예시 코드를 바로 실행해보세요.
송신자는 수신자가 직접 데이터를 받을때까지 대기하지 않고 버퍼에 값을 보내기만 하면 다음 코드를 실행하기 때문에 훨씬 효율이 높아집니다. 물론 버퍼가 가득 차서 더이상 송신할 수 없을 때는 다음 코드를 실행하지 않고 채널에 묶여버립니다.(무한 대기 상태) 또한 main() 함수의 수신 루틴은 한개 받고 한개를 처리할 필요 없이 버퍼에 값이 있으면 바로바로 꺼내 씁니다. 똑같이 더이상 버퍼에 값이 송신되지 않으면 수신 루틴은 무한 대기 상태가 되고 main() 함수에서는 데드락이 발생합니다. 따라서 송/수신 채널의 개수를 잘 맞춰줘야합니다.
구름IDE를 사용해 송/수신 채널의 개수를 수정해서 출력 값을 확인하고 결괏값이 어떻게 출력되는지 확인하세요.