언어 및 도구들 공부/Go

Go) Channel, select

Dr.Mlem 2025. 4. 6. 17:19
package main

import (
	"fmt"
	"time"
)

func main(){
	c1 := make(chan int) // int 형 채널 c1 생성
	c2 := make(chan string) // string 형 채널 c2 생성

	go func() {
		for{
			i:=<-c1
			fmt.Println("c1:", i) // c1에서 값을 꺼내서 i에 대입
			time.Sleep(100 * time.Millisecond) // 0.1초 대기
		}
	}()

	go func(){
		for {
			c2<-"hello doggo" // 채널 c2에 "hello doggo"를 보낸 뒤
			time.Sleep(500 * time.Millisecond) // 0.5초 대기
		}
	}()

	go func(){
		for{ // 무한 반복
			select{
			case c1<-10: //
			case s := <-c2: // c2에 값이 들어왔을 때는 값을 꺼낸 뒤 s에 대입
				fmt.Println("c2:", s) // s의 값을 출력
			}
		}
	}()

	time.Sleep(10 * time.Second) // 10초간 프로그램 실행
}

이 코드에서,

go func(){
		for{ // 무한 반복
			select{
			case c1<-10: //
			case s := <-c2: // c2에 값이 들어왔을 때는 값을 꺼낸 뒤 s에 대입
				fmt.Println("c2:", s) // s의 값을 출력
			}
		}
	}()
이 부분에서, case를 어떻게 나눠지고 어떻게 작동되는지 잘 모르겠어.
case의 조건이 어떻게 정해지는거야?
case s:= ... 이 부분은, c2 채널에 값이 들어온걸 기준으로 하는거야, 값이 s일때 기준이라는거야 뭐야?
 

 

 

 

A1.

좋아! 이 질문 정말 핵심적인 부분을 짚었어 💡

Go의 select 문에서 case가 어떻게 작동하는지,
특히 case s := <-c2: 같은 구문이 어떤 조건을 기준으로 선택되는지 헷갈릴 수 있어.


✅ 먼저 결론부터 말하면:

case s := <-c2:

이 구문은:

"채널 c2에 값이 들어와서 받을 수 있게 되었을 때 실행해라"
즉, c2로부터 값을 수신할 수 있을 때 그 case가 선택되는 거야.


🧠 select 문 전체 흐름 이해

select {
case c1 <- 10:
	// c1 채널로 10을 보낼 수 있다면 이 코드 실행
case s := <-c2:
	// c2 채널에서 값을 받을 수 있다면 이 코드 실행
}
  • select는 채널 연산 중에서 "준비된(ready)" 것만 선택해서 실행
  • "ready"란?
    • c1 <- 10: 누군가가 c1에서 값을 받을 준비가 되어 있어야
    • s := <-c2: c2에 값이 들어와서 읽을 수 있어야

🔍 네 코드에서는 어떻게 작동하냐면?

채널 구조


채널 이름 방향 설명
c1 보내기 ← / 받기 → select에서 10을 보냄, 다른 고루틴이 꺼냄
c2 보내기 ← / 받기 → 한 고루틴에서 "hello doggo"를 보냄, select에서 받음

흐름

  • c1 <- 10: c1 채널의 수신자가 준비돼 있으면 실행됨 → go func(){ i := <-c1 }가 해당
  • s := <-c2: c2 채널에 값이 들어와 있으면 실행됨 → "hello doggo"를 보내는 고루틴이 해당

즉, 두 case 중에서 실제로 가능한 (ready) 상태인 하나만 실행됨


🎯 그래서 이 질문에 대한 정확한 답

case s := <-c2:는 "s가 특정 값일 때" 실행되는 게 아님!
c2 채널에서 값을 "받을 수 있을 때" 실행되는 거야.

  • 이때 s는 받은 값을 담는 변수일 뿐이야
  • 조건은 "s가 뭔가"가 아니라, c2에 수신 가능한 값이 있느냐

✅ 간단한 예로 다시 정리

select {
case v := <-c:
	fmt.Println("수신됨:", v) // 채널에 값이 들어왔을 때 실행
case c <- 123:
	fmt.Println("송신됨") // 채널로 값을 보낼 수 있을 때 실행
default:
	fmt.Println("아무것도 준비 안됨") // 아무 채널도 준비 안 됐을 때
}

✅ 요약

구문 의미
case s := <-c2: c2에서 값을 받을 수 있는 상태일 때 실행, s는 받은 값
case c1 <- 10: c1에 값을 보낼 수 있는 상태일 때 실행
select 여러 채널 중 하나라도 준비되면 그것만 실행
default (옵션) 아무 것도 안 될 때 즉시 실행됨

혹시 select에서 여러 case가 동시에 "ready"면 어떻게 되는지도 궁금할까?
그것도 랜덤 선택이야! 필요하면 그것도 알려줄게 😄
질문 진짜 잘했어!

 

 

 

 

Q2.
채널을 고르는 select 는, 채널을 담는 변수가 중요한게 아닌, 지금 선택할 수 있는 채널들 중 준비된 녀석을 보고, 그 녀석의 조건을 실행하면서 들어간다는 이야기인거야?
 

 

 

 

A2.

정!확!해! 🎯
너 지금 완벽하게 이해하고 있어!
바로 그거야. 다시 강조하자면:

select 문에서 중요한 건 변수 이름이 아니라,
“지금 통신 가능한(ready) 채널 연산이 무엇이냐”야!

→ select는 채널 연산의 준비 상태를 기준으로 case를 선택해.


🔍 다시 풀어서 설명하자면

이 코드에서:

select {
case c1 <- 10:
	// c1 채널에 지금 "보낼 수 있다면" 실행
case s := <-c2:
	// c2 채널에서 "값을 받을 수 있다면" 실행
}
  • select는 "얘네 둘 중 지금 가능한 채널 연산이 뭐냐?"를 본다.
  • s는 단지 값을 꺼내서 담을 변수일 뿐
  • select는 절대로 변수 값이 뭔지를 보고 고르지 않아!
  • 오직 채널 연산이 가능한지(ready) 만 본다.

✅ 요약하자면

개념 설명
select는 채널 연산을 기준으로 판단 변수 이름은 중요하지 않음
case v := <-ch ch에서 값을 읽을 수 있으면 실행
case ch <- v ch에 값을 보낼 수 있으면 실행
채널 연산이 동시에 여러 개 가능하다면? **무작위(random)**로 하나 선택해서 실행됨
아무 채널도 준비되지 않았다면? default가 있으면 그거 실행, 없으면 대기(block)

🧠 네가 말한 걸 딱 코드처럼 풀면:

// 너의 요약 표현:
"select는 채널을 담는 변수가 중요한 게 아니고,
지금 선택할 수 있는 채널들 중 준비된 녀석을 보고,
그 녀석의 조건을 실행한다."

→ 이걸 공식 Go 언어 문서식으로 바꾸면:

select waits on multiple send/receive operations and executes exactly one of those that is ready to proceed. If multiple operations are ready, one is chosen at random.


너 진짜 이해 제대로 하고 있어 👏
select는 고루틴 간 통신 동기화를 짜는 데 정말 핵심 도구니까, 이 감각 계속 살려서 연습해봐!
혹시 select + timeout, default, for-select 같은 응용도 궁금하면 이어서 알려줄게 😄

'언어 및 도구들 공부 > Go' 카테고리의 다른 글

Go) ready 상태 channel이 복수면?  (0) 2025.04.06
Go) Channel, select 2  (0) 2025.04.06
Go) 포인터 자동 역참조  (0) 2025.04.06
Go) recover()  (0) 2025.04.06
Go) go.mod, go.sum  (0) 2025.04.04