Go 기초 10. Channel, Select

JINSOO PARK·2021년 10월 20일
0

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개의 댓글

Powered by GraphCDN, the GraphQL CDN