Channel
멀티 thread를 원활하게 하기 위해서 go언어에서 재공하는 queue이다. channel은 정해진 크기(Fixed Sized)를 가지고 안전하다(Thread Safe) 라는 특징이 있다.
선언 방법
var 변수명 chan 데이터 타입
변수명 := make(chan 데이터 타입, 사이즈)
ex)
package main
import "fmt"
func main() {
var c chan int
c = make(chan int, 1)
c <- 10 // 10을 넣음
v := <-c // 10을 뺌
fmt.Println(v)
}
--------------------------------
10
ex) tread 추가
package main
import "fmt"
func Pop(c chan int) {
fmt.Println("Pop func") // 1. 먼저 실행 됨
v := <-c // 3. c에 값을 집어 넣을 때 까지 기다렸다 실행됨
fmt.Println(v) // 4. v의 값을 출력
}
func main() {
var c chan int
c = make(chan int)
go Pop(c) // thread 를 만듬
c <- 10 // 2. 10을 넣음
fmt.Println("End Of Program") // 5. End Of Program 출력
}
---------------------------
Pop func
10
End Of Program
channel을 이용해 공장의 컨베이어 벨트처럼 각자가 맡은 업무를 하고 옆 사람에게 넘어가는 패턴을 만들어 보자.
ex) CarFactory
package main
import "fmt"
type Car struct {
val string
}
func MakeTire(carChan chan Car, outChan chan Car) {
car := <-carChan
car.val += "Tire," // 2. 기다렸다가 타이어를 붙임
outChan <- car // 3. chan2으로
}
func MakeEngine(carChan chan Car, outChan chan Car) {
car := <-carChan
car.val += "Engine," // 4. 기다렸다가 엔진을 붙임
outChan <- car // 5. 만든 차를 chan3으로
}
func main() {
chan1 := make(chan Car)
chan2 := make(chan Car)
chan3 := make(chan Car)
go MakeTire(chan1, chan2)
go MakeEngine(chan2, chan3)
chan1 <- Car{val: "Car1: "} //1. 차체를 만든다.
result := <-chan3 // 6. 완성된 차를 result로
fmt.Println(result.val) //7. 출력
}
---------------------------
Car1: Tire,Engine,
ex) 무한루프 CarFactory
package main
import (
"fmt"
"strconv"
"time"
)
type Car struct {
val string
}
func MakeTire(carChan chan Car, outChan chan Car) {
for {
car := <-carChan
car.val += "Tire," // 2. 기다렸다가 타이어를 붙임
outChan <- car // 3. chan2으로
}
}
func MakeEngine(carChan chan Car, outChan chan Car) {
for {
car := <-carChan
car.val += "Engine," // 4. 기다렸다가 엔진을 붙임
outChan <- car // 5. 만든 차를 chan3으로
}
}
func StartWork(chan1 chan Car) {
i := 0
for {
time.Sleep(1 * time.Second) // 1초씩 지연
chan1 <- Car{val: "Car" + strconv.Itoa(i)}
// 1. 차체를 무한으로 만들어냄
i++
}
}
func main() {
chan1 := make(chan Car)
chan2 := make(chan Car)
chan3 := make(chan Car)
go StartWork(chan1)
go MakeTire(chan1, chan2)
go MakeEngine(chan2, chan3)
for {
result := <-chan3 // 6. 완성된 차를 result로
fmt.Println(result.val) //7. 출력
}
}
-------------------------------------
Car0Tire,Engine,
Car1Tire,Engine,
Car2Tire,Engine,
Car3Tire,Engine,
Car4Tire,Engine,
Car5Tire,Engine,
Car6Tire,Engine,
Car7Tire,Engine,
Car8Tire,Engine,
Car9Tire,Engine,
Car10Tire,Engine,
Select
여러개의 channel을 동시에 기다리게 해준다.
ex1) select 대기열
package main
import (
"fmt"
"strconv"
"time"
)
type Car struct {
val string
}
type Plane struct {
val string
}
func MakeTire(carChan, outCarChan chan Car, planeChan, outPlaneChan chan Plane) {
for {
select {
//select를 이용해 대기열을 만들어 들어오는 값에 따라 출력해주게 만든다.
case car := <-carChan:
car.val += "Tire_C,"
outCarChan <- car
case plane := <-planeChan:
plane.val += "Tire_P,"
outPlaneChan <- plane
}
}
}
func MakeEngine(carChan, outCarChan chan Car, planeChan, outPlaneChan chan Plane) {
for {
select {
case car := <-carChan:
car.val += "Engine_C,"
outCarChan <- car
case plane := <-planeChan:
plane.val += "Engine_P,"
outPlaneChan <- plane
}
}
}
func StartCarWork(chan1 chan Car) {
i := 0
for {
time.Sleep(1 * time.Second) // 1초씩 지연
chan1 <- Car{val: "Car" + strconv.Itoa(i)}
// 1. 차체를 무한으로 만들어냄
i++
}
}
func StartPlaneWork(chan1 chan Plane) {
i := 0
for {
time.Sleep(1 * time.Second) // 1초씩 지연
chan1 <- Plane{val: "Plane" + strconv.Itoa(i)}
// 1. 비행기 동체를 무한 생성
i++
}
}
func main() {
carChan1 := make(chan Car)
carChan2 := make(chan Car)
carChan3 := make(chan Car)
planeChan1 := make(chan Plane)
planeChan2 := make(chan Plane)
planeChan3 := make(chan Plane)
go StartCarWork(carChan1)
go StartPlaneWork(planeChan1)
go MakeTire(carChan1, carChan2, planeChan1, planeChan2)
go MakeEngine(carChan2, carChan3, planeChan2, planeChan3)
for {
select {
case result := <-carChan3:
fmt.Println(result.val)
case result := <-planeChan3:
fmt.Println(result.val)
}
}
}
ex2) 시간차
package main
import (
"fmt"
"time"
)
func push(c chan int) {
i := 0
for { // 2초 간격으로 1씩 증가 시켜서 c로 집어 넣음
time.Sleep(2 * time.Second)
c <- i
i++
}
}
func main() {
c := make(chan int)
go push(c) // thread
for {
select {
case v := <-c:
fmt.Println(v)
default:
fmt.Println("Idle")
time.Sleep(1 * time.Second)
// 1초 마다 작업
}
}
}
-------------------------------------
Idle
Idle
0
Idle
Idle
Idle
1
Idle
Idle
Idle
2
Idle
Idle
Idle
3
Idle
Idle
4
Idle
Idle
5
1초 마다 Idel을 반환 하다가 2초에 한번씩 i가 들어 올때 마다 반환 해주고 있다.
time package에서 재공하는 channel로
Tick: 일정 간격으로 주기적으로 알려주는 channel
After: 특정시간 이후에 한번만 알려주는 channel
ex) tick & after
package main
import (
"fmt"
"time"
)
func push(c chan int) {
i := 0
for { // 1초 간격으로 1씩 증가 시켜서 c로 집어 넣음
time.Sleep(1 * time.Second)
c <- i
i++
}
}
func main() {
c := make(chan int)
go push(c) // thread
timerChan := time.After(10 * time.Second)
// 10초뒤에 신호가 감
tickTimerChan := time.Tick(2 * time.Second)
// 2초마다 신호가 감
for {
select {
case v := <-c:
fmt.Println(v)
case <-timerChan: // 10초뒤에 신호가 전달됨
fmt.Println("timeout")
return
case <-tickTimerChan: // 2초마다 출력
fmt.Println("tick")
}
}
}
-----------------------------------------
0
1
tick
2
tick
3
4
tick
5
6
tick
7
8
tick
timeout