Programming/golang2026. 8. 25. 10:55

기본적으로 채널은 양방향이고, 생성시에 방향을 지정할수 있다

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

chan T          // can be used to send and receive values of type T
chan<- float64  // can only be used to send float64s
<-chan int      // can only be used to receive ints

[링크 : https://go.dev/ref/spec#Channel_types]

[링크 : https://mynewcodings.tistory.com/313]

[링크 : https://jh-labs.tistory.com/716]

 

채널 브릿징

[링크 : https://kr.linkedin.com/pulse/mastering-go-concurrency-comprehensive-guide-channel-sourav-choudhary-0nwbc?tl=ko]

 

 

+

2026.08.26

ch chan<- string 이라는 건 chan<- string 타입의 ch 변수로 해석을 하면 되는데

$ cat ch.go
package main

import "fmt"

// sendOnly: ch1에는 보내기만 가능
func sender(ch string chan<-) {
ch <- "안녕하세요!"
}

// recvOnly: ch1에서 받기만 가능
func receiver(ch <-chan string) {
msg := <-ch
fmt.Println(msg)
}

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
receiver(ch)    // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨
}

 

ch string chan<- 을 허용하나 바꾸고 빌드해보는데 에러가 난다. 칫.

$ go build ch.go
# command-line-arguments
./ch.go:6:23: syntax error: unexpected keyword chan in parameter list; possibly missing comma or )

 

+

의도적으로 go를 붙이지 않아 고루틴으로 하지 않고 받는 걸 먼서 생성하고

블럭킹 되어있어서 보내는걸 실행하지 못하게 하면

$ cat ch.go 
package main

import "fmt"

// sendOnly: ch1에는 보내기만 가능
func sender(ch chan<- string) {
ch <- "안녕하세요!"
}

// recvOnly: ch1에서 받기만 가능
func receiver(ch <-chan string) {
msg := <-ch
fmt.Println(msg)
}

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨
sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
// go sender(ch) // 혹은 이렇게
}

 

빌드 시에는 아무런 에러나 경고가 없지만

실행시에는 deadlock이라고 띄우준다. 오.. 좋은데?

$ ./ch
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.receiver(0x2a1f3ecc6738?)
/home/falinux/work/src/go/ch.go:12 +0x1e
main.main()
/home/falinux/work/src/go/ch.go:19 +0x26

 

둘다 go 붙여서 돌리면 받는 쪽이 느린건지 이상하게(?) 아무런 메시지도 나오지 않는다. 머지?

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨
go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
}

 

그래서  sleep 추가해도 안되네?

claude 말로는 둘다 go 루틴 실행하고 main()의 끝에 도달해서 종료되는 바람에 그렇다고 한다.

$ cat ch.go 
package main

import (
"fmt"
"time"
)

// sendOnly: ch1에는 보내기만 가능
func sender(ch chan<- string) {
time.Sleep(100)
ch <- "안녕하세요!"
}

// recvOnly: ch1에서 받기만 가능
func receiver(ch <-chan string) {
msg := <-ch
fmt.Println(msg)
}

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨
go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
}

 

그래서 아래와 같이 main 함수를 늦게 종료하면 정상적으로 잘 나온다.

많이는 하지 않았는데 놓치지 않는걸 보면 

받는 쪽에서는 블로킹 되어있고, 채널 자체는 큐에 차있다가 뺴갈수 있을때 전달이 되는 듯.

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨
go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨

time.Sleep(1000 * time.Millisecond)
}

 

 

+

양방향 전달은 의외로(?) 별거 없다.

그냥 chan<- <-chan 에서 <- 지우고 선언후, 당연하게(?) 보내고 받으면 된다.

$ cat ch.go 
package main

import (
"fmt"
"time"
)

// sendOnly: ch1에는 보내기만 가능
func sender(ch chan string) {
ch <- "안녕하세요!"

msg := <-ch
fmt.Println(msg)
}

// recvOnly: ch1에서 받기만 가능
func receiver(ch chan string) {
msg := <-ch
fmt.Println(msg)

ch <- "느그아부지 모하시노!"
}

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
go receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨

time.Sleep(1000 * time.Millisecond)
}

 

$ ./ch
안녕하세요!
느그아부지 모하시노!

 

+

len() 함수로 현재 채널 버퍼의 크기를 확인할 수 있고

cap() 함수로 채널 버퍼의 최대 크기를 확인할 수 있다

$ cat ch_cap.go 
package main

import "fmt"

func main() {
ch := make(chan int, 5) // 버퍼 크기 5인 채널 생성

ch <- 1
ch <- 2
ch <- 3

fmt.Println("현재 버퍼에 쌓인 개수:", len(ch)) // 3
fmt.Println("버퍼 전체 용량:", cap(ch))       // 5
}

$ go run ch_cap.go
현재 버퍼에 쌓인 개수: 3
버퍼 전체 용량: 5

[링크 : https://mynewcodings.tistory.com/313]

 

그런데 이상하게도 채널 크기를 지정하지 않으면 에러가 발생한다

$ cat ch_cap.go 
package main

import "fmt"

func main() {
ch := make(chan int) // 버퍼 크기 5인 채널 생성

ch <- 1
ch <- 2
ch <- 3

fmt.Println("현재 버퍼에 쌓인 개수:", len(ch)) // 3
fmt.Println("버퍼 전체 용량:", cap(ch))       // 5
}

$ go run ch_cap.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
/home/falinux/work/src/go/ch_cap.go:8 +0x36
exit status 2

 

claude 답변으로는 capacity를 지정하지 않으면 길이가 0이고, 보내는데 받는 쪽이 없어서 deadlock 발생한다고 판단한다고.

즉, 버퍼가 없지만 받아서 소비하지 않으면 계속 블러킹 되는건가?

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.

[링크 : https://go.dev/ref/spec#Channel_types]

 

그런데 그렇게 보기에는..

얘는 왜 컴파일도 문제 없고, 실행시에도 아무런 메시지 출력 안되고 (deadlock 안걸리고) 잘 종료되지?

claude 답변으로는 deadlock은 실제로는 쓰레드가 모두 잠들어서 깨지 못하는 상황을 탐지하는거라

main thread가 1초 뒤에 깰꺼고 "기브니 쪼꼬렛!"은 전송되지 못하고 기다리다 종료되는거라고

$ cat ch.go
package main

import (
"fmt"
"time"
)

// sendOnly: ch1에는 보내기만 가능
func sender(ch chan string) {
ch <- "안녕하세요!"
ch <- "기브미 쪼꼬렛!"

msg := <-ch
fmt.Println(msg)
}

// recvOnly: ch1에서 받기만 가능
func receiver(ch chan string) {
msg := <-ch
fmt.Println(msg)

ch <- "느그아부지 모하시노!"
}

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
// go receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨

time.Sleep(1000 * time.Millisecond)
}

 

실제로 단계별로 해보면 "안녕하세요!" 보내고 멈춰버렸다. 그렇군.

$ cat ch.go
package main

import (
"fmt"
"time"
)

// sendOnly: ch1에는 보내기만 가능
func sender(ch chan string) {
fmt.Println("run")
ch <- "안녕하세요!"
fmt.Println("wait")
ch <- "기브미 쪼꼬렛!"
fmt.Println("oops")

msg := <-ch
fmt.Println(msg)
}

// recvOnly: ch1에서 받기만 가능
func receiver(ch chan string) {
msg := <-ch
fmt.Println(msg)

ch <- "느그아부지 모하시노!"
}

func main() {
ch := make(chan string) // 생성할 때는 항상 양방향으로 만듦

go sender(ch)   // sender 함수 안에서는 송신만 가능하도록 타입이 제한됨
// go receiver(ch) // receiver 함수 안에서는 수신만 가능하도록 타입이 제한됨

time.Sleep(1000 * time.Millisecond)
}

 

$ go run ch.go
run

 

 

+

자꾸 헷갈리게 되는데

switch-case가 있고 select-case가 있다

select-case는 채널 전용 으로 생긴건 거의 유사한데

저렇게 값을 던지고 안보고 종료하는 용도로도 쓸 수 있나 보다.

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

[링크 : https://go.dev/tour/concurrency/5]

 

[링크 : https://go.dev/doc/effective_go#control-structures]

[링크 : https://go.dev/doc/effective_go#leaky_buffer]

 

claude 말로는 select에서 block 되어있어서

이를 이용해 타임아웃을 구현하는데 쓸 수 있다고 한다.

package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan string)

go func() {
time.Sleep(2 * time.Second)
ch <- "작업 완료"
}()

select {
case result := <-ch:
fmt.Println(result)
case <-time.After(1 * time.Second): // 1초 안에 ch가 준비 안 되면 여기 실행
fmt.Println("타임아웃!")
}
}

'Programming > golang' 카테고리의 다른 글

golang mutex  (0) 2026.08.25
golang Init()  (0) 2026.08.20
golang git commit hash  (0) 2025.11.24
golang 정적웹 파일 포함하기  (0) 2025.11.24
go vet (golang 정적분석)  (0) 2025.10.02
Posted by 구차니