Go 기초 10. Channel, Select

JINSOO PARK·2021년 10월 20일

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

Produce Consumer 패턴 만들기

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가 들어 올때 마다 반환 해주고 있다.


tick과 after를 이용한 select

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
profile
개린이

0개의 댓글