Study Memory Work
[Go Lang] 채널 Channel 본문
func main() {
ch1 := make(chan int)
ch2 := make(chan string)
go func() {
for { // 채널이 닫힐 때 까지 반복
ch1 <- 77
time.Sleep(250 * time.Millisecond)
}
}()
go func() {
for { // 채널이 닫힐 때 까지 반복
ch2 <- "HIHI"
time.Sleep(500 * time.Millisecond)
}
}()
go func() {
for {
select {
case num := <-ch1:
fmt.Println("ch1 : ", num)
case str := <-ch2:
fmt.Println("ch2 : ", str)
// default: // 타이밍 문제로 채널을 이용해 select문을 사용할 때는 default 사용하지 말 것!
// fmt.Println("default test")
}
}
}()
time.Sleep(7 * time.Second) // 7초 뒤에 main 종료
}
Channel
- 고루틴간의 상호 정보(데이터) 교환 및 실행 흐름 동기화(동기식)를 위해 사용
- 수신이 완료될 때까지 대기하고, 데이터가 수신이 되면 진행한다. - 실행 흐름을 제어 가능(동기, 비동기 모두) -> 일반 변수로 선언하려 사용
- 데이터 전달 자료형 선언 후 사용해야 함.(지정된 타입만 주고받을 수 있음)
- interface{} 전달을 통해 자료형 상관없이 전송 및 수신 가능
- 값을 전달(복사 후 : bool, int 들) , 포인터(슬라이즈, 맵) 등을 전달할 때는 주의
- 멀티프로세싱 처리에서 교착상태(경쟁) 주의
- '<-', '->' 사용.
- '채널 ->' : 수신
- '채널 <- 데이터' : 송신
채널 선언
// 1.
var c chan int
c = make(chan int)
// 2.
v := make(chan int)
채널 - 동기화 예제
func work11(v chan int) {
fmt.Println("Work1 : Start --->", time.Now())
time.Sleep(1 * time.Second)
fmt.Println("Work1 : End --->", time.Now())
v <- 1
}
func work22(v chan int) {
fmt.Println("Work2 : Start --->", time.Now())
time.Sleep(1 * time.Second)
fmt.Println("Work2 : End --->", time.Now())
v <- 2
}
func main() {
fmt.Println("Main : Start ---> ", time.Now())
v := make(chan int)
go work11(v)
go work22(v)
<-v
<-v // 이 두 채널이 마지막까지 모든 데이터의 수신을 대기하기 때문에 main thread가 먼저 끝날일 없음
fmt.Println("Main : End ---> ", time.Now())
}
출력 : (time.Sleep을 쓰지 않아도 main이 고루틴이 다 돈 후 종료된 것을 볼 수 있다.) Main : Start ---> 2023-03-06 13:53:19.398981 +0900 KST m=+0.000235168 Work2 : Start ---> 2023-03-06 13:53:19.399308 +0900 KST m=+0.000562209 Work1 : Start ---> 2023-03-06 13:53:19.399313 +0900 KST m=+0.000567376 Work1 : End ---> 2023-03-06 13:53:20.400448 +0900 KST m=+1.001706418 Work2 : End ---> 2023-03-06 13:53:20.400424 +0900 KST m=+1.001682626 Main : End ---> 2023-03-06 13:53:20.400547 +0900 KST m=+1.001804876 |
채널 Buffer
- 버퍼 : 양에 제한 없이 계속 데이터를 보낼 수 있고, chan을 생성할 때 그 범위를 정할 수 있다.
ch := make(chan bool, 4) // 4개가 가득찰 때까지 받고, 가득차면 비운다
- 송신 측 : 버퍼가 가득찰 때까지 계속 보냄. 가득차면 대기
- 수신 측 : 버퍼가 빌 때 까지 계속 데이터를 받음. 비어있으면 대기
func main() {
runtime.GOMAXPROCS(1) // 갯수 지정
ch := make(chan bool, 4) // 채널 버퍼의 크기 지정
cnt := 20
go func() {
for i := 0; i < cnt; i++ {
ch <- true // 발신
fmt.Println("Go : ", i)
}
}()
for i := 0; i < cnt; i++ {
<- ch // 수신
fmt.Println("Main : ", i)
}
}
출력 : ( 지정한 버퍼의 갯수만큼 보내고, 비우고를 반복하는 것을 볼 수 있다.) Go : 0 Go : 1 Go : 2 Go : 3 Go : 4 Main : 0 Main : 1 Main : 2 Main : 3 Main : 4 Main : 5 Go : 5 Go : 6 Go : 7 Go : 8 Go : 9 Go : 10 Main : 6 Main : 7 Main : 8 ... |
채널 Close
- 채널은 사용 후 닫고 반환해주어야 함. 그렇지 않으면 over됨
- range가 있을 경우 close하지 않으면 무한 대기한다.
func main() {
ch := make(chan bool)
go func() {
for i := 0; i < 5; i++ {
ch <- true
}
close(ch)
}()
// range ch --> ch 채널이 닫힐 때까지 수신 대기한다!
for i := range ch {
fmt.Println("ex1 : ", i)
}
}
수신전용 채널, 발신전용 채널
- Go는 수신 전용 채널과 발신 전용 채널을 구분한다
- 발신 전용 : channel <- 데이터형
- 수신 전용 : <- channel
- 매개변수를 통해서 전용 채널이 무엇인 지 확인할 수 있다.
- 채널 또한 함수의 반환 값으로 사용이 가능하다
// 발신전용 표시
func sendOlny(c chan<- int, cnt int) {
for i := 0; i < 10; i++ {
c <- i
}
c <- 777
// fmt.Println(<-c) -> 발신전용에 수신하려고 하면 오류남!!
}
// 수신전용 표시
func receiveOnly(c <-chan int) {
for i := range c {
fmt.Println("received : ", i)
}
}
func main() {
c := make(chan int)
go sendOlny(c, 10) // 발신전용
go receiveOnly(c) // 수신전용
time.Sleep(time.Second * 2)
}
채널을 Return 값으로 사용하기
func sum(cnt int) <-chan int { // 채널을 return 값으로 설정
sum := 0
tot := make(chan int)
go func() {
for i := 1; i < cnt; i++ {
sum += i
}
tot <- sum // 전체 sum값을 채널에 발신
}()
return tot // 데이터를 발신한 상태의 채널을 return 값으로 보냄
}
func main() {
//1
c := sum(100)
fmt.Println("ex1 : ", <-c)
}
출력 : ex1 : 4950 |
func receiveOnly(cnt int) <-chan int {
sum := 0
tot := make(chan int)
go func() {
for i := 0; i < cnt; i++ {
sum += i
}
tot <- sum
tot <- 777
tot <- 777
close(tot) // 닫아버렸기 때문에 tot 채널은 값만 들어있는 수신 전용 채널이 됨.
}()
return tot // 수신 전용 채널 반환
}
// 수신 전용 메소드를 파라미터로 받고 return도 하는 메소드
func total(c <-chan int) <-chan int {
tot := make(chan int)
go func() {
a := 0
for i := range c {
a += i
}
tot <- a
close(tot) // tot에 a를 담고 닫아버려 수신전용 채널로 만듬
}()
return tot
}
func main() {
cc := receiveOnly(100) //채널 반환
output := total(cc) //채널 전달 후 반환
// output<- 10 --> Error!!!
fmt.Println("ex2 : ", <-output)
}
출력 : ex2 : 6504 |
channel select 구문
- channel에서 값이 수신되면 case 문 실행.
func main() {
ch1 := make(chan int)
ch2 := make(chan string)
go func() {
for { // 채널이 닫힐 때 까지 반복
ch1 <- 77
time.Sleep(250 * time.Millisecond)
}
}()
go func() {
for { // 채널이 닫힐 때 까지 반복
ch2 <- "HIHI"
time.Sleep(500 * time.Millisecond)
}
}()
go func() {
for {
select {
case num := <-ch1:
fmt.Println("ch1 : ", num)
case str := <-ch2:
fmt.Println("ch2 : ", str)
// default: 타이밍 문제로 채널을 이용해 select문을 사용할 때는 default 사용하지 말 것!
// fmt.Println("default test")
}
}
}()
time.Sleep(7 * time.Second) // 7초 뒤에 main 종료
}
출력: ch1 : 77 ch2 : HIHI ch1 : 77 ch1 : 77 ch2 : HIHI ch1 : 77 ch1 : 77 ch2 : HIHI ch1 : 77 ch2 : HIHI ch1 : 77 ch1 : 77 ch2 : HIHI ch1 : 77 ... (7초간 진행) |
'Programing > Go' 카테고리의 다른 글
[Go Lang] Git에서 Go 프로젝트를 Clone했을 때 go.mod 생성하기 (0) | 2023.03.30 |
---|---|
[Go Lang] gocron을 이용하여 스케줄러 만들기 (0) | 2023.03.29 |
[Go Lang] 고루틴 - 멀티코어, 클로저 사용하기 (0) | 2023.03.06 |
[Go Lang] 인터페이스 (0) | 2023.03.02 |
[Go Lang] 구조체 - 임베디드 패턴, 메소드 상속 활용, 오버라이딩 (0) | 2023.03.02 |