728x90

프로그래밍의 입문 딱지를 떼고나서 마주쳤던 몇가지 문제들중 멀티쓰레드와 클로저에 대해서 피보나치로 파헤쳐 보려고합니다.

 

동시성을 이용한 구현

func fibonacci(max int) <-chan int {
	c := make(chan int)
	go func() {
		defer close(c)
		a, b := 0, 1
		for a <= max {
			c <- a
			a, b = b, a+b
		}
	}()
	return c
}

 

클로저를 이용한 구현

func fibonacci(max int) <-chan int {
	c := make(chan int)
	go func() {
		defer close(c)
		a, b := 0, 1
		for a <= max {
			c <- a
			a, b = b, a+b
		}
	}()
	return c
}

 

실행

func main() {
	for fib := range fibonacci(15) {
		fmt.Printf("%d, ", fib)
	}

	fmt.Println()

	fib := fibonacciClosure(15)
	for n := fib(); n >= 0; n = fib() {
    fmt.Printf("%d, ", n)
	}
}

 

둘다 같은 값을 출력합니다.

 

일단 클로저 부터 파헤쳐 보자면 Go 언어에서 함수는 Closure로서 사용될 수도 있습니다.

Closure는 함수 외부의 변수를 참조하는 함수값(fucntion value)를 일컫는데 이때 함수의 바깥의 변수를 마치 함수안에서 그대로 그 변수를 읽거나 쓸수 있게 됩니다.

 

그래서 처음 initalize 를 하고나서 다른 변수에 다시 initalize 하지 않는 이상 그변수가 가르키는 값을 변하지 않습니다.

 

Goroutine 으로 구현한 피보나치를 보겠습니다. generate pattern 을 사용하여 receive 만 가능한 채널을 리턴합니다.

 

fibonacci 의 내부의 c라는 채널은 양방향으로 선언되어있지만 return 값은 one way입니다.

return value 를 양방향으로 줄수도 있지만 one way 로 준 이유는 외부에서

fibonacci(3) <- 3

으로 채널에 데이터를 보내게 될경우 받는 고루틴이 없어서 Deadlock 에 걸리게 됩니다.

 

실수를 미연에 방지하고자 return value 를 강제합니다.

 

차이점

비슷해보이지만 Go에서는 채널을 이용하는 방법에 몇가지 장점이 있습니다.

1. 생성하는 쪽에서 상태 저장 방법을 복잡하게 고민할 필요가 없다.

2. 받는 쪽에서 for 의 range를 이용할 수 있다.

3. 채널 버퍼를 이용하면 멀티 코어를 활용하거나 입출력 성능상의 장점을 이용할 수 있다.

 

728x90